This page last changed on Oct 25, 2005 by jnolen.

I've spent the last few days looking at Confluence's memory footprint.

The biggest win so far (besides turning off all the caches ) has been in Spring.

Confluence uses dependency injection everywhere, both for its services and to initialise short lived objects like xwork actions. It seems that Spring doesn't directly cater for the latter use, and is very easy to misuse if you aren't careful.

Spring keeps track of dependencies between the beans it manages, so if you inject bean A into bean B, Spring will record the fact. Spring will call B.setA(A) of course, to perform the injection. Then it adds the name of B to the list of beans which depend on A, so that during shutdown it can remove B before A.

Confluence autowires beans using the DefaultListableBeanFactory.autowireBeanProperties() method. This assumes that the bean is a singleton, and registers it. It also doesn't check whether the bean it is registering is already a dependent of the bean being injected. So the linked list of dependencies grows with every page view. Five hundred views go it up to almost 10MB!

Spring does allow non-singleton beans, and it understands that they shouldn't be registered as dependents, but DefaultListableBeanFactory doesn't provide a way of autowiring a non-singleton bean.

I created a new factory to do the job:

private static class BucketListableBeanFactory extends DefaultListableBeanFactory
    {
        public BucketListableBeanFactory(ApplicationContext context)
        {
            super(context);
        }

        public void autowireNonSingletonBeanProperties(Object existingBean, int autowireMode, boolean dependencyCheck)
                throws BeansException
        {
            if (autowireMode != AUTOWIRE_BY_NAME && autowireMode != AUTOWIRE_BY_TYPE)
            {
                throw new IllegalArgumentException("Just constants AUTOWIRE_BY_NAME and AUTOWIRE_BY_TYPE allowed");
            }
            RootBeanDefinition bd = new RootBeanDefinition(existingBean.getClass(), autowireMode, dependencyCheck);
            bd.setSingleton(false);
            populateBean(existingBean.getClass().getName(), bd, new BeanWrapperImpl(existingBean));
        }
    }

That's just a copy of autowireBeanProperties(), with the addition of a call to setSingleton().

Confluence's calisthenics and orthodontia is under way. Soon we'll be running light without overbyte!

Document generated by Confluence on Oct 10, 2007 18:36