Uploaded image for project: 'Spring Framework'
  1. Spring Framework
  2. SPR-17079

Spring-context optimization: LinkedMultiValueMap and ArrayList should be initialized with specified capacity for preventing collections from resizing

    XMLWordPrintable

Details

    • true

    Description

      Spring-context library should be tuned in memory allocation point of view.
      We use JMC and JFR API for aggregating memory allocation events.
      Minor GC could be tuned with help of investigating of memory allocation events inside new TLAB.
      Major GC could be tuned with help of investigating of memory allocation events outside TLAB.
      We focused on reducing Minor GC overhead.

      Following Setup Categories used:

      spring.interceptor.resolveCaches org.springframework.cache.interceptor.AbstractCacheResolver.resolveCaches()
      spring.interceptor.CacheAspectSupport org.springframework.cache.interceptor.CacheAspectSupport.execute()
      spring.expression org.springframework.expression.spel.CodeFlow
      spring.core org.springframework.core.BridgeMethodResolver.findBridgedMethod()
      
      logger.info org.slf4j.impl.Log4jLoggerAdapter.info()
      logger.info weblogic.i18n.logging.NonCatalogLogger.info()
      
      logger.warning weblogic.i18n.logging.NonCatalogLogger.warning()
      spring.beans org.springframework.beans.factory.support.DefaultListableBeanFactory.getBean()
      spring.aop org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.<allocate inside tlab>()
      spring.aop org.springframework.aop.aspectj.MethodInvocationProceedingJoinPoint.<allocate inside tlab>()
      spring.aop org.springframework.aop.aspectj.RuntimeTestWalker$ThisInstanceOfResidueTestVisitor.<allocate inside tlab>()
      

      1. AbstractCacheResolver.resolveCaches

      "org.springframework.cache.interceptor.AbstractCacheResolver, since 4.1"
      package org.springframework.cache.interceptor;
      
      
      public abstract class AbstractCacheResolver implements CacheResolver, InitializingBean {
      
      	@Override
      	public Collection<? extends Cache> resolveCaches(CacheOperationInvocationContext<?> context) {
      		Collection<String> cacheNames = getCacheNames(context);
      		if (cacheNames == null) {
      			return Collections.emptyList();
      		}
      		else {
      			Collection<Cache> result = new ArrayList<Cache>();
      			for (String cacheName : cacheNames) {
      				Cache cache = this.cacheManager.getCache(cacheName);
      				if (cache == null) {
      					throw new IllegalArgumentException("Cannot find cache named '" +
      							cacheName + "' for " + context.getOperation());
      				}
      				result.add(cache);
      			}
      			return result;
      		}
      	}
      

      Here a screenshot of diff between 4.1.x and 5.0.x versions:

      Need specify ArrayList capacity right in creation.

      2. CacheAspectSupport$CacheOperationContexts.init()

      "org.springframework.cache.interceptor.CacheAspectSupport$CacheOperationContexts , since 3.1"
      package org.springframework.cache.interceptor;
      
      public abstract class CacheAspectSupport extends AbstractCacheInvoker
      		implements InitializingBean, SmartInitializingSingleton, ApplicationContextAware {
      
      
      	private class CacheOperationContexts {
      
      		private final MultiValueMap<Class<? extends CacheOperation>, CacheOperationContext> contexts =
      				new LinkedMultiValueMap<Class<? extends CacheOperation>, CacheOperationContext>();
      
      		public CacheOperationContexts(Collection<? extends CacheOperation> operations, Method method,
      				Object[] args, Object target, Class<?> targetClass) {
      
      			for (CacheOperation operation : operations) {
      				this.contexts.add(operation.getClass(), getOperationContext(operation, method, args, target, targetClass));
      			}
      		}
      
      		public Collection<CacheOperationContext> get(Class<? extends CacheOperation> operationClass) {
      			Collection<CacheOperationContext> result = this.contexts.get(operationClass);
      			return (result != null ? result : Collections.<CacheOperationContext>emptyList());
      		}
      	}
      
      

      Here a screenshot of diff between 4.1.x and 5.0.x versions:

      Should be added capacity for Map. it help reduce resize and unnecessary memory allocation.

      Possible solutions:

      "org.springframework.cache.interceptor.CacheAspectSupport"
      	private class CacheOperationContexts {
      
      		private final MultiValueMap<Class<? extends CacheOperation>, CacheOperationContext> contexts;// =
      				//new LinkedMultiValueMap<Class<? extends CacheOperation>, CacheOperationContext>();
      
      		public CacheOperationContexts(Collection<? extends CacheOperation> operations, Method method,
      				Object[] args, Object target, Class<?> targetClass) {
      			contexts = new LinkedMultiValueMap<Class<? extends CacheOperation>, CacheOperationContext>( (int) Math.round( operations.size()/0.75) );
      			for (CacheOperation operation : operations) {
      				this.contexts.add(operation.getClass(), getOperationContext(operation, method, args, target, targetClass));
      			}
      		}
      
      		public Collection<CacheOperationContext> get(Class<? extends CacheOperation> operationClass) {
      			Collection<CacheOperationContext> result = (contexts != null? this.contexts.get(operationClass): null );
      			return (result != null ? result : Collections.<CacheOperationContext>emptyList());
      		}
      	}
      
      

      +

      "org.springframework.cache.interceptor.AbstractCacheResolver"
      	@Override
      	public Collection<? extends Cache> resolveCaches(CacheOperationInvocationContext<?> context) {
      		Collection<String> cacheNames = getCacheNames(context);
      		if (cacheNames == null) {
      			return Collections.emptyList();
      		}
      		else {
      			Collection<Cache> result = new ArrayList<Cache>( (int) Math.round(cacheNames.size() ) );
      			for (String cacheName : cacheNames) {
      				Cache cache = this.cacheManager.getCache(cacheName);
      				if (cache == null) {
      					throw new IllegalArgumentException("Cannot find cache named '" +
      							cacheName + "' for " + context.getOperation());
      				}
      				result.add(cache);
      			}
      			return result;
      		}
      	}
      

      Attachments

        1. AbstractCacheResolver.bak
          3 kB
        2. AbstractCacheResolver.java
          3 kB
        3. CacheAspectSupport.bak
          24 kB
        4. CacheAspectSupport.java
          24 kB
        5. screenshot-1.png
          screenshot-1.png
          61 kB
        6. screenshot-2.png
          screenshot-2.png
          110 kB
        7. screenshot-3.png
          screenshot-3.png
          102 kB
        8. screenshot-4.png
          screenshot-4.png
          72 kB

        Issue Links

          Activity

            People

              juergen.hoeller Juergen Hoeller
              alexofo83i Alexander Fedorov
              Spring Issues Spring Issues
              Votes:
              0 Vote for this issue
              Watchers:
              2 Start watching this issue

              Dates

                Created:
                Updated:
                Resolved:
                3 years, 47 weeks, 2 days ago