Skip to content

Allow clearing all caches to avoid classloader leaks #4953

Closed
@jin-harmoney

Description

@jin-harmoney

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();

Metadata

Metadata

Assignees

No one assigned

    Labels

    2.19Issues planned at 2.19 or later

    Type

    No type

    Projects

    No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions