Fix a bug in ConstructorCache when classes are GC'ed but not removed from cache #6186
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Fix a bug in ConstructorCache when classes are GC'ed but not removed from cache
Motivation and Context
The ConstructorCache uses WeakReferences to hold Class objects, which can be garbage-collected, leading to Optional.empty() results even though the class may still be load-able. The ConstructorCache is using a WeakHashMap which handles GC of the keys only, but not when values (the class Objects in this case) are cleaned up.
The current design of the constructor cache was introduced to store the classloader and class with a weak reference, so that we don't prevent them from being unloaded. It was intended to follow We do something similar in DynamoDB enhanced's BeanTableSchema but, unlike the BeanTableSchema, this cache also uses weakreferences to the values being cached.
Modifications
Adds an additional check in the
getClass
method to check if the referent in the WeakReference has been garbage collected and if so, remove it from the cache.Q: Why not use a
ReferenceQueue
to handle removals as in the implementation of WeakHashMap?Short answer: it increases complexity more than is justified to handle an edge case error. Long answer: The schema of the cache (specially
Map<String, Map<ClassLoader, Optional<WeakReference<Class<?>>>>>
) makes this more difficult to track - we would need to create a new entry class that extends WeakReference and stores the className and classLoader and would need to ensure the reference to classLoader is also a weak reference in this entry. We would then need to add synchronization around check the reference queue and logic to remove cache entries from the nested cache correctly.Testing
Testing this edge case is extremely difficult - I was able to manually reproduce the issue with a hacked custom class loader + multiple threads, but its not consistently reproducible, so I have not added an automated unit test to cover this case.
Types of changes
Checklist
mvn install
succeedsscripts/new-change
script and following the instructions. Commit the new file created by the script in.changes/next-release
with your changes.License