Description
JNA version: 5.13.0
$java --version
openjdk 17.0.7 2023-04-18
OpenJDK Runtime Environment (build 17.0.7+7-Ubuntu-0ubuntu122.04.2)
OpenJDK 64-Bit Server VM (build 17.0.7+7-Ubuntu-0ubuntu122.04.2, mixed mode, sharing)
Issue
We're running into a memory leak that is related to com.sun.jna.internal.Cleaner
, when using JNA in a web app servlet that is served by Tomcat.
Cleaner
starts a daemon thread called JNA Cleaner
, which is not shut down upon undeploying the servlet. The thread will get Tomcat's custom/dedicated class loader for the servlet as its contextClassLoader
(an instance of Tomcat's ParallelWebappClassLoader
).
This classloader maintains references to all the classes it loaded (i.e. the entire webapp), and so all these are prevented from being GC'ed.
Workaround
I've managed to construct a hacky workaround that solves the problem, but it's not pretty...
It uses a sneaky throw of an InterruptedException
in a cleanup task which is registered to the cleaner instance in the webapp's contextDestroyed
method.
private static class JnaCleanerDestroyer implements Runnable {
@lombok.SneakyThrows
@Override
public void run() {
throw new InterruptedException();
}
}
In the webapp's contextDestroyed
:
var ref = new Object();
var cleaner = com.sun.jna.internal.Cleaner.getCleaner();
cleaner.register(ref, new JnaCleanerDestroyer());
ref = null;
This works as long as the Cleaner impl. does not change the way it handles InterruptedException
, but it's obviously messy.
Feature request
Could a more permanent solution be realized? E.g. a method dispose()
on Cleaner
, that causes the thread to be shut down.