6

When I using spring framework to write business code, I always use the singleton scope and avoid using the prototype scope. I think there is a performance difference between prototype and singleton. Because of the prototype scope, spring creates a new instance every call. And I think it is slower than using the singleton scope. Am I right? And am I giving too many considerations to the performance?

tkruse
  • 10,222
  • 7
  • 53
  • 80
Lauda Wang
  • 869
  • 3
  • 11
  • 19
  • 1
    If you are worried about the object instantiation itself: Then yes, you are giving this too much consideration. The only really difference would be if there is some heavy logic in that prototype (and it would be redundant to do it over and over again). For example if you created a new database connection every time. Use Singleton where appropriate (i.e. re-usable throughout the application lifecycle), use Prototype where appropriate (i.e. contains something that is specific to each call/request/use). – Thilo Jun 16 '19 at 03:24
  • https://stackoverflow.com/questions/21969044/when-to-use-spring-prototype-scope – Thilo Jun 16 '19 at 03:26
  • It is possible that only one bean is created and used even with the prototype scope, depending on how you wire your application. So your question is not detailed enough. – tkruse Aug 18 '19 at 01:23

3 Answers3

2

Yes, you are right prototype scoped beans consumes more resources. According to the documentation:

The non-singleton prototype scope of bean deployment results in the creation of a new bean instance every time a request for that specific bean is made.

The constructor is called at each request instead of only once for a singleton scoped bean. But there is another aspect to take into account. The documentation says:

Thus, although initialization lifecycle callback methods are called on all objects regardless of scope, in the case of prototypes, configured destruction lifecycle callbacks are not called. The client code must clean up prototype-scoped objects and release expensive resources that the prototype beans hold. To get the Spring container to release resources held by prototype-scoped beans, try using a custom bean post-processor, which holds a reference to beans that need to be cleaned up.

In some respects, the Spring container’s role in regard to a prototype-scoped bean is a replacement for the Java new operator. All lifecycle management past that point must be handled by the client.

If you want to avoid an out of memory exception, you have to take care yourself of the release of all resources (such as database connections) hold by the prototype scoped beans.

Unless it's really needed, it's better to use singleton scoped beans.

Ortomala Lokni
  • 56,620
  • 24
  • 188
  • 240
  • 1
    @Ortamola "you have to take care yourself of beans destruction." Do you really mean writing code to 'nullify' the beans object. Can the GC not do this? – veritas Feb 24 '20 at 05:54
  • This answer is misleading. Of course java's garbage collector also cleans prototype-beans if no reference exists to the created object. The quoted documentation only states that the usual lifecycle callback methods won't be called after the point where instantiation is completed. That means prototype-beans can't use these methods to clean up / close / release expensive objects. – david Apr 11 '22 at 10:46
  • @david I have rephrased the the second to last sentence of my answer. I hope, it's no more misleading. – Ortomala Lokni Apr 11 '22 at 19:18
2

I can relate to the question, I have been researching it recently. Construction of objects is very cheap, unless they actually do some heavy lifting in the constructor. You never think twice about constructing a bunch of lambdas in one statement. That's not the problem.

The problem is Spring's inefficiency, the way it wasn't architectured right. I have recently benchmarked Spring vs Guice. Singletons are slightly faster with Spring, but Prototypes are 20 times slower.

From code sanity point of view, you should always prefer Prototypes. Guice defaults to prototypes. javax.inject.Inject documentation defaults to prototypes. Spring lists some vague historical reasons and does Singletons.

Prototypes are safer because they cannot accidentally store state and use it in the wrong context. They cannot store "userId" from one request and use it in another, since with Prototypes, a brand new instance with clean state gets created every time. That's how I learned this pattern: we cached the wrong user context accidentally when using Singletons with RequestScoped Providers. Ouch. That mistake is important to avoid. Getting CPU is a much smaller problem.

To summarize: use Prototypes for better code, and dont use Spring if performance is very important.

iluxa
  • 6,941
  • 18
  • 36
1

I don't think you'll see any noticeable performance difference between prototypes and singletons in general in the most basic sense.

It's true that there is only one singleton bean instance in the application context, and instances of beans defined with "prototype" scope are created for every request to that bean, however, we're talking about the performance of new object creation and its very cheap these days and optimized for non-expensive objects.

However here are some caveats:

What if the Constructor of the bean calls some pretty expensive code, its not spring's fault of course, because the programmer wrote the code this way, but still, actually, some pretty expensive code will be called every time the bean is created and it can drain the performance.

Slightly more interesting: we usually don't place code like this in consturctor, instead we use "lifecyle" methods like @PostConstruct / @PreDestroy.

For Singletons spring will call the post-construct method once when it creates a singleton and after that will place the bean onto the application context. When the application context gets closes (usually when the app shuts down) the pre-destroy method is called to free up the resources, etc.

However, with Prototype scoped beans it's not the case: Spring creates them upon every request to the bean, then calls the post-construct method, again for every single instance of the bean it creates, then doesn't store the instance in the application context. This means that spring doesn't call at all pre-destroy methods (because it doesn't have a clue who are these objects, it doesn't really manage them after they're created). So this can (again, not spring's fault) lead to serious performance differences between the two.

If we'll take it further, sometimes spring has to wrap the object into some sort of proxy. These proxies are usually done with java.lang.Proxy in case of interfaces or with Cglib which is based on inheritance. Now this fact alone, when we're talking about wrapping prototype-scoped beans into proxy, can become a source of the serious difference in performance because Proxies are generated in runtime and Wrapping an Object with proxy (especially with CGlib) is pretty expensive.

Mark Bramnik
  • 39,963
  • 4
  • 57
  • 97