Skip to content

Cleaner (thread) related memory leak #1521

Closed
@cardamon

Description

@cardamon

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.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions