package org.stagemonitor.core.instrument; import net.bytebuddy.agent.builder.AgentBuilder; import net.bytebuddy.pool.TypePool; import org.stagemonitor.core.util.ExecutorUtils; import java.util.concurrent.Executors; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.TimeUnit; /** * This {@link net.bytebuddy.agent.builder.AgentBuilder.TypeLocator} caches * {@link net.bytebuddy.description.type.TypeDescription}s and clears the cache every minute to avoid memory leaks. * <p/> * Class loader memory leaks are also avoided by using {@link WeakConcurrentMap}. */ public class AutoEvictingCachingBinaryLocator extends AgentBuilder.PoolStrategy.WithTypePoolCache { private final WeakConcurrentMap<ClassLoader, TypePool.CacheProvider> cacheProviders = new WeakConcurrentMap .WithInlinedExpunction<ClassLoader, TypePool.CacheProvider>(); private final ScheduledExecutorService executorService; public AutoEvictingCachingBinaryLocator() { this(TypePool.Default.ReaderMode.EXTENDED); } public AutoEvictingCachingBinaryLocator(TypePool.Default.ReaderMode readerMode) { super(readerMode); executorService = Executors.newScheduledThreadPool(1, new ExecutorUtils.NamedThreadFactory("type-pool-cache-evicter")); executorService.scheduleAtFixedRate(new Runnable() { @Override public void run() { cacheProviders.clear(); TimedElementMatcherDecorator.logMetrics(); } }, 5, 1, TimeUnit.MINUTES); } @Override protected TypePool.CacheProvider locate(ClassLoader classLoader) { classLoader = classLoader == null ? ClassLoader.getSystemClassLoader() : classLoader; TypePool.CacheProvider cacheProvider = cacheProviders.get(classLoader); while (cacheProvider == null) { cacheProvider = TypePool.CacheProvider.Simple.withObjectType(); TypePool.CacheProvider previous = cacheProviders.putIfAbsent(classLoader, cacheProvider); if (previous != null) { cacheProvider = previous; } } return cacheProvider; } /** * Shuts down the internal thread pool */ public void close() { executorService.shutdown(); } }