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

    • Last commented by a User:
      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

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

                Dates

                Created:
                Updated:
                Resolved:
                Days since last comment:
                3 years, 31 weeks, 2 days ago