-
Notifications
You must be signed in to change notification settings - Fork 798
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
MemoryAllocationExports Not Collected By Garbage Collector And Causing Metaspace OutOfMemoryException #809
Comments
Thanks a lot for the detailed analysis and for finding this bug! I agree we should add a Some thoughts on how this could be implemented:
Anyway, as you have already figured out the issue implementation seems pretty straightforward. Do you want to open a PR? Note: I renamed |
Just a shower thought: Maybe there is a way to allow clean-up without an explicit If we had a wrapper around the listener like this: static class Listener implements NotificationListener {
private final WeakReference<NotificationListener> delegate;
private Listener(NotificationListener listener) {
this.delegate = new WeakReference<>(listener);
}
@Override
public void handleNotification(Notification notification, Object handback) {
NotificationListener listener = delegate.get();
if (listener == null) {
for (GarbageCollectorMXBean garbageCollectorMXBean : ManagementFactory.getGarbageCollectorMXBeans()) {
if (garbageCollectorMXBean instanceof NotificationEmitter) {
try {
((NotificationEmitter) garbageCollectorMXBean).removeNotificationListener(this);
} catch (ListenerNotFoundException ignored) {
}
}
}
} else {
listener.handleNotification(notification, handback);
}
}
} If we load this class in a new class loader with the system class loader as its parent, then the Web application can be collected. Then I didn't try it, maybe it doesn't work at all. What do you think? |
Thank you very much for the quick and detail feedback. I think the point about multiple calls to Besides, the Java Doc of the class explicitly points out that the initialisation method should be used for multiple calls. Also, from my point of view, multiple calls would be a wrong usage of the public interface. And yes, the library could restrict the incorrect usage, but in this case the approach should be considered consistently for the whole library and not just for a single method. But yes, this additional logic could be added. The idea with the ClassLoader could work, but in my view it is a too deep intervention in the system. The An alternative solution, which is also platform-dependent and which I also refrain from, would be the way via a Due to that I would stick to If you agree with the |
Yes, let's go ahead with the I thought about the classloader idea again, but it's harder than I thought. It's easy to write a custom classloader for loading the |
I wrote above
I'm not sure if that's really a good idea. I guess it makes sense if you can prevent |
Hi @fstab, are there still open points from my side? |
Hi @Drophoff , sorry, I dropped the ball here. We just released the 1.0.0 version, and the MemoryAllocationExports moved to https://github.com/prometheus/client_java/blob/main/prometheus-metrics-instrumentation-jvm/src/main/java/io/prometheus/metrics/instrumentation/jvm/JvmMemoryPoolAllocationMetrics.java. However, the code is still the same, so your PR should still be relevant. I'm heading to PromCon for the rest of the week. I'll put this on top of my todo list for beginning of next week. Sorry again for dropping the ball and thanks a lot for pinging me again. |
This problem with releasing metrics is one of issues that prevents http4s adapter from being upgraded easily. If object that groups metrics into bigger units existed - one for this particular group of metrics should simply implement AutoCloseable. And its to caller to instantiate it, and its to caller to release it when not needed. Even the collector registration to the registry can be wrapped as resource (closing "registration" resource should unregister underlying collector) - but to make it possible it is needed to operate on composite Collectors rather than unrelated metrics built by some common code.
Just reconsider #904 and #905. Maybe I'll add sample how this can work to PR. |
Actual Situation
The class MemoryAllocationExports creates a memory leak caused by AllocationCountingNotificationListener that is added but never removed, when this libary is included within a WAR application that gets deployed on a Tomcat server.
The AllocationCountingNotificationListener is added to the class GarbageCollectorMXBean (see class GarbageCollectorExtIml at the attached screenshot) that is loaded by java.lang.ClassLoader, which is different from the org.apache.catalina.loader.ParallelWebappClassLoader.
Because of this the AllocationCountingNotificationListener has a reference to a class with a different classloader, which prevents that this class gets collected by garbage collector.
Possible solution (outlook)
At the moment the class MemoryAllocationExports provides no function to retrieve a instance of AllocationCountingNotificationListener, which is needed to call NotificationEmitter#removeNotificationListener:
When the above code gets called the class is collected and clean up by the garbage collector.
The class DefaultExports contains already a function for initialization. Possibly this class would also be suitable to provide a corresponding function for the clean shutdown, which could be called during ServletContextListener#contextDestroyed.
Environment
The text was updated successfully, but these errors were encountered: