Description
Is your feature request related to a problem? Please describe.
I'm using both hypersistence-utils and jackson in my Quarkus project and I see that my application classloader is not garbage collected on live-reload.
This seems to be caused by the static ObjectMapper in ObjectMapperWrapper.
The ObjectMapper is not loaded by the application classloader, but it does contain references to classes of the my application (custom deserializers etc). Hence, it blocks the garbage collection of the application classloader.
I described a similar case for hibernate-envers here.
Describe the solution you'd like
As mentioned in another issue, a clear()
method on ObjectMapper to evict all caches before the application shuts down would probably solve this.
Usage example
Specifically when using hypersistence-utils, it would be ObjectMapperWrapper.INSTANCE.getObjectMapper().clear();
. That code would be placed in a method annotated with @Shutdown
.
Additional context
No response
Workaround
// Hypersistence utils uses an object mapper. The object mapper has several caches that keep references to classes and cause classloader memory leaks.
var hyObjectmapper = ObjectMapperWrapper.INSTANCE.getObjectMapper();
hyObjectmapper.getTypeFactory().clearCache();
// mapper._rootDeserializers is private, so we need to clear it using reflection
var field = hyObjectmapper.getClass().getDeclaredField("_rootDeserializers");
field.setAccessible(true);
var rootDeserializers = (Map) field.get(hyObjectmapper);
rootDeserializers.clear();
// DeserializationContext._cache is private, so we need to clear it using reflection
field = hyObjectmapper.getDeserializationContext().getClass().getSuperclass().getSuperclass().getDeclaredField("_cache");
field.setAccessible(true);
var cache = (DeserializerCache) field.get(hyObjectmapper.getDeserializationContext());
cache.flushCachedDeserializers();
// Flush other stuff
((DefaultSerializerProvider) ObjectMapperWrapper.INSTANCE.getObjectMapper().getSerializerProvider()).flushCachedSerializers();