Announcement Announcement Module
Collapse
No announcement yet.
Performance issue while instantiating prototype beans Page Title Module
Move Remove Collapse
X
Conversation Detail Module
Collapse
  • Filter
  • Time
  • Show
Clear All
new posts

  • Performance issue while instantiating prototype beans

    Hi,

    Sorry, I am new to spring and new to this forum. Feel free to criticize anything.

    I am using spring 3.0.5.

    My overall issue is that I need to instantiate numerous (Let's say dozens or hundreds of thousands) prototype beans from several singleton beans. I don't know the instantiation count at application startup. The application need to be very fast (But should rely on spring).

    In the following code, the singleton bean "Foo" need to create "count" of "MyBean" when its "perform" method is called.
    Three different ways are used to create the "MyBean" instances.
    1/ Using beanFactory.getBean(String). "FooImpl" implements BeanFactoryAware so it can use it to retrieve an instance by name.
    2/ Using beanFactory.getBean(Class). Instead of using the bean name, the class is used.
    3/ Using myBeanFactory.getObject(). An ObjectFactory<MyBean> is injected using @Autowired. So it is used to create instances.

    Code:
    @Configuration
    public class MyConfiguration
    {
    	@Bean
    	public Foo foo()
    	{
    		return new FooImpl();
    	}
    
    	@Bean
    	@Scope("prototype")
    	public MyBean myBean()
    	{
    		return new MyBeanImpl();
    	}
    }
    
    public class FooImpl implements Foo, BeanFactoryAware
    {
    	@Autowired
    	private ObjectFactory<MyBean> myBeanFactory;
    
    	private BeanFactory beanFactory;
    
    	public void perform(int count)
    	{
    		SimpleChrono chrono1 = new SimpleChrono("BeanFactory, by name");
    		for (int i = 0; i < count; i++)
    		{
    			MyBean myBean = (MyBean)beanFactory.getBean("myBean");
    		}
    		chrono1.stop();
    
    		SimpleChrono chrono2 = new SimpleChrono("BeanFactory, by class");
    		for (int i = 0; i < count; i++)
    		{
    			MyBean myBean = (MyBean)beanFactory.getBean(MyBean.class);
    		}
    		chrono2.stop();
    
    		SimpleChrono chrono3 = new SimpleChrono("ObjectFactory");
    		for (int i = 0; i < count; i++)
    		{
    			MyBean myBean = myBeanFactory.getObject();
    		}
    		chrono3.stop();
    	}
    
    	@Override
    	public void setBeanFactory(BeanFactory beanFactory) throws BeansException
    	{
    		this.beanFactory = beanFactory;
    	}
    }
    It is written in several places on the web that getBean(String) is not supposed to be very fast. It is even in the old javadoc.
    Note that callers should retain references to returned objects. There is no guarantee that this method will be implemented to be efficient. For example, it may be synchronized, or may need to run an RDBMS query.
    I thought it would be a difficult task for getBean(String) to find the corresponding class to instantiate basing on the configuration field name.
    I thought that getBean(Class) would be faster.
    And I thought that the dedicated factory would be far more faster. Because it is dedicated. The needed class is known at compilation time, the factory has to provide instances of a single class... The first getObject() could be slow, but others would be damn fast.

    I was all wrong. Here are the results (For count = 10000):

    12/01/12 18:06:47 DEBUG Starting 'BeanFactory, by name'.
    12/01/12 18:06:48 DEBUG End of 'BeanFactory, by name' 00:00:01:125
    12/01/12 18:06:48 DEBUG Starting 'BeanFactory, by class'.
    12/01/12 18:07:08 DEBUG End of 'BeanFactory, by class' 00:00:20:187
    12/01/12 18:07:08 DEBUG Starting 'ObjectFactory'.
    12/01/12 18:07:29 DEBUG End of 'ObjectFactory' 00:00:20:516
    getBean(String) is twelve times faster than others methods...
    Why the hell?

    Here is profiler screenshot for getObject.

    The method 3 with a dedicated factory seems really cleaner to me. But due to performance issue, I will have do use getbean(String)?

    MyBean could be a classical java class, not used as a bean with spring dependency injection. So I would have to manually give to MyBean its dependency. What are the others drawbacks with this solution ?

    Is there others ways to solve this problem?
    Last edited by rt15; Jan 23rd, 2012, 08:24 AM.

  • #2
    More investigations on this issue.

    At runtime, the injected "ObjectFactory" (private ObjectFactory<MyBean> myBeanFactory) is an instance of DependencyObjectFactory.
    It is injected in resolveDependency method of DefaultListableBeanFactory.
    The constructor of DependencyObjectFactory takes a descriptor of the field (Field type (ObjectFactory<MyBean>), field name...) and the "beanName", which is actually the name of the bean in which the injection is made.

    The constructor of DependencyObjectFactory extract the class that must be generated by the factory in the field descriptor using reflection:
    ObjectFactory<MyBean> -> MyBean

    Then the getObject method of DependencyObjectFactory is simply implemented as follow:
    Code:
    public Object getObject() throws BeansException {
    	return doResolveDependency(this.descriptor, this.type, this.beanName, null, null);
    }
    Nothing is cached. Each time getObject is called then doResolveDependency is called.
    As MyBean is not an array, a map or anything, doResolveDependency is calling findAutowireCandidates.

    Then findAutowireCandidates is calling BeanFactoryUtils.beanNamesForTypeIncludingAncestor s.
    beanNamesForTypeIncludingAncestors find beans names that corresponds to the requested type.
    MyBean class -> "myBean" string.
    This is a very time consuming operation in our case (See profiler result in my previous message).

    Then, with the result of beanNamesForTypeIncludingAncestors, findAutowireCandidates create a Map<String, Object>.
    The string key contains the name of the bean.
    The value is the an instance corresponding to the bean name.
    The instance is created using getBean, so:
    beanFactory.getBean("myBean").

    This is why dedicated factories are really slower than using getBean with a bean name.
    Dedicated factories are first finding the bean name from the type, which is slow.
    Then they are calling the getBean method.

    To be as fast as getbean(String), DependencyObjectFactory should call beanNamesForTypeIncludingAncestors only once and cache the result (The bean name) as a field of DependencyObjectFactory.
    Then it would call getBean with this cached information.

    But DependencyObjectFactory should be even faster than getBean.
    It should cache more information about the bean that must be created (For example it could cache RootBeanDefinition and call createBean. But it may be deeper than that.).
    Last edited by rt15; Jan 23rd, 2012, 07:25 AM.

    Comment


    • #3
      I con't be of much help with what you have listed here.

      But from what I saw, if you are just learning Spring now, I highly highly recommend using up to Spring documentation and examples. Something like Spring in Action 3rd Edition, or the Spring 3.0.5 documentation online.

      For instance, since Spring 2.5 you would use an ApplicationContext and not a BeanFactory to start Spring. Also, you would rarely if ever need to declare a bean to be prototype.

      I think in the long run, you will be better off. If you stick with using old stuff, then you will learn bad habits that are easily fixed with today's version of Spring.

      I hope that makes sense. I would hate you having spend so much time learning old style Spring and not be productive and have to relearn everything you just learned.

      Good Luck

      Mark

      Comment


      • #4
        Sounds like a RTFM response. Obviously, I should read the manual. But it is really huge. I read only small parts I am interested in, and I am not sure that it was up to date information.

        We are using an ApplicationContext to startup the application. So we may should use ApplicationContextAware instead of BeanFactoryAware. But anyway, the ApplicationContext is using a DefaultListableBeanFactory internally.

        I will take a look at method injection. It seems to be now the recommended way to solve multiple prototypes instantiation.

        Thanks for the repsonse that will make me investigate this.

        Comment


        • #5
          Lookup method injection is not supported by annotations/component scanning while we are using AnnotationConfigApplicationContext...

          But there is a comment on this issue:
          I was really interested in that option till I understood @ScopedProxy annotation.
          The elegant work around is making the singleton autowire a ScopeProxy which has a prototype behavior.

          Not saying that this shouldn't be implemented but maybe priority is not as important.
          I'll take a look at this too.

          Comment


          • #6
            I cannot use scope either.
            It appears to allow to have a different instance per method call while I need a lot of instances within a single method call.

            Anyway, I believe that even spring injection of dependencies in the created instances (The @Autowired handling) would be too slow for me. There is simply too much time spent in AbstractAutowireCapableBeanFactory.createBean/doCreateBean/populateBean...

            My conclusion is that I cannot use spring to instantiate many prototype "beans". Instead, I will try to use light java object instantiated with "new". Treatment of these objects would be done by stateless services singleton beans created using spring.
            With that approach, time spent in spring should be mainly isolated at application startup (In AbstractApplicationContext.refresh.).

            Comment


            • #7
              The main question is why do you have to have a bean be prototype. If you bean is a domain object that holds state, then you don't want Spring to create those for you and you wouldn't make it a bean. What is typically in a config for beans are those stateless beans where having just one instance (singleton bydefault) is exactly what you need. a single stateless bean does not have thread issues and can handle as many threads calling the same method simultaneous as the operating system will allow.

              So exactly what you said in your last post's last sentence.

              Great job coming up with that conclusion on your own.

              Mark

              Comment

              Working...
              X