3737import java .util .Map ;
3838import java .util .concurrent .ConcurrentMap ;
3939import java .util .concurrent .TimeUnit ;
40- import java .util .concurrent .atomic .AtomicLong ;
4140
4241/**
4342 * Caches {@link TypeDescription}s which speeds up type matching -
4645 * Without a type pool cache those types would have to be re-loaded from the file system if their {@link Class} has not been loaded yet.
4746 * <p>
4847 * In order to avoid {@link OutOfMemoryError}s because of this cache,
49- * the {@link TypePool.CacheProvider}s are wrapped in a {@link SoftReference}
48+ * the {@link TypePool.CacheProvider}s are wrapped in a {@link SoftReference}.
49+ * If the underlying cache is being GC'ed, a new one is immediately being created instead, so to both prevent OOME and
50+ * enable continuous caching.
51+ * Any cache that is not being accessed for some time (configurable through constructor), is being cleared, so that
52+ * caches don't occupy heap memory until there is a heap stress. This fits the typical pattern of most type matching
53+ * occurring at the beginning of the agent attachment.
5054 * </p>
5155 */
5256public class SoftlyReferencingTypePoolCache extends AgentBuilder .PoolStrategy .WithTypePoolCache {
@@ -58,6 +62,7 @@ public class SoftlyReferencingTypePoolCache extends AgentBuilder.PoolStrategy.Wi
5862 */
5963 private final WeakConcurrentMap <ClassLoader , CacheProviderWrapper > cacheProviders =
6064 new NullSafeWeakConcurrentMap <ClassLoader , CacheProviderWrapper >(false );
65+
6166 private final ElementMatcher <ClassLoader > ignoredClassLoaders ;
6267
6368 public SoftlyReferencingTypePoolCache (final TypePool .Default .ReaderMode readerMode ,
@@ -80,16 +85,13 @@ protected TypePool.CacheProvider locate(ClassLoader classLoader) {
8085 return TypePool .CacheProvider .Simple .withObjectType ();
8186 }
8287 classLoader = classLoader == null ? getBootstrapMarkerLoader () : classLoader ;
83- CacheProviderWrapper cacheProviderRef = cacheProviders .get (classLoader );
84- if (cacheProviderRef == null || cacheProviderRef .get () == null ) {
85- cacheProviderRef = new CacheProviderWrapper ();
86- cacheProviders .put (classLoader , cacheProviderRef );
88+ CacheProviderWrapper cacheProviderWrapper = cacheProviders .get (classLoader );
89+ if (cacheProviderWrapper == null ) {
90+ cacheProviders .put (classLoader , new CacheProviderWrapper ());
8791 // accommodate for race condition
88- cacheProviderRef = cacheProviders .get (classLoader );
92+ cacheProviderWrapper = cacheProviders .get (classLoader );
8993 }
90- final TypePool .CacheProvider cacheProvider = cacheProviderRef .get ();
91- // guard against edge case when the soft reference has already been cleared since evaluating the loop condition
92- return cacheProvider != null ? cacheProvider : TypePool .CacheProvider .Simple .withObjectType ();
94+ return cacheProviderWrapper ;
9395 }
9496
9597 /**
@@ -115,8 +117,9 @@ protected TypePool.CacheProvider locate(ClassLoader classLoader) {
115117 */
116118 void clearIfNotAccessedSince (long clearIfNotAccessedSinceMinutes ) {
117119 for (Map .Entry <ClassLoader , CacheProviderWrapper > entry : cacheProviders ) {
118- if (System .currentTimeMillis () >= entry .getValue ().getLastAccess () + TimeUnit .MINUTES .toMillis (clearIfNotAccessedSinceMinutes )) {
119- cacheProviders .remove (entry .getKey ());
120+ CacheProviderWrapper cacheWrapper = entry .getValue ();
121+ if (System .currentTimeMillis () >= cacheWrapper .getLastAccess () + TimeUnit .MINUTES .toMillis (clearIfNotAccessedSinceMinutes )) {
122+ cacheWrapper .clear ();
120123 }
121124 }
122125 }
@@ -125,21 +128,49 @@ WeakConcurrentMap<ClassLoader, CacheProviderWrapper> getCacheProviders() {
125128 return cacheProviders ;
126129 }
127130
128- private static class CacheProviderWrapper {
129- private final AtomicLong lastAccess = new AtomicLong (System .currentTimeMillis ());
130- private final SoftReference <TypePool .CacheProvider > delegate ;
131+ private static class CacheProviderWrapper implements TypePool .CacheProvider {
132+
133+ private volatile long lastAccess = System .currentTimeMillis ();
134+ private volatile SoftReference <TypePool .CacheProvider > delegate ;
131135
132136 private CacheProviderWrapper () {
133- this . delegate = new SoftReference <TypePool .CacheProvider >(new TypePool . CacheProvider . Simple ());
137+ delegate = new SoftReference <TypePool .CacheProvider >(new Simple ());
134138 }
135139
136140 long getLastAccess () {
137- return lastAccess .get ();
141+ return lastAccess ;
142+ }
143+
144+ private TypePool .CacheProvider getDelegate () {
145+ TypePool .CacheProvider cacheProvider = delegate .get ();
146+ if (cacheProvider == null ) {
147+ synchronized (this ) {
148+ cacheProvider = delegate .get ();
149+ if (cacheProvider == null ) {
150+ cacheProvider = new Simple ();
151+ delegate = new SoftReference <TypePool .CacheProvider >(cacheProvider );
152+ }
153+ }
154+ }
155+ return cacheProvider ;
138156 }
139157
158+ @ Override
140159 @ Nullable
141- TypePool .CacheProvider get () {
142- return delegate .get ();
160+ public TypePool .Resolution find (String name ) {
161+ lastAccess = System .currentTimeMillis ();
162+ return getDelegate ().find (name );
163+ }
164+
165+ @ Override
166+ public TypePool .Resolution register (String name , TypePool .Resolution resolution ) {
167+ lastAccess = System .currentTimeMillis ();
168+ return getDelegate ().register (name , resolution );
169+ }
170+
171+ @ Override
172+ public void clear () {
173+ getDelegate ().clear ();
143174 }
144175 }
145176
0 commit comments