question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

ScanResult leaks shutdown hooks

See original GitHub issue

This code shows that a shutdown hook (ie unstarted thread) is created but not destroyed for each scan, even when using try-with-resources for ScanResult.

Seems like Runtime.getRuntime().removeShutdownHook() might be needed. [EDIT: was addShutdownHook, nonsensically.]

import java.lang.reflect.Field;
import java.util.Map;

import io.github.classgraph.ClassGraph;
import io.github.classgraph.ScanResult;

public class ClassGraphShutdownHooks {
    public static void main(String[] args) throws Exception {
        for (int i = 0; i < 100_000; i++) {
            try (ScanResult scan = new ClassGraph().scan()) {
            }
            System.out.printf("Number of shutdown hooks: %d%n", countShutdownHooks());
        }
    }

    // thanks to https://stackoverflow.com/a/6865509/14379
    // works on Java 8
    private static int countShutdownHooks() throws ReflectiveOperationException {
        Class clazz = Class.forName("java.lang.ApplicationShutdownHooks");
        Field field = clazz.getDeclaredField("hooks");
        field.setAccessible(true);
        Map<?, ?> hooks = (Map<?, ?>) field.get(null);
        return hooks.size();
    }
}

Output:

Number of shutdown hooks: 1
Number of shutdown hooks: 2
Number of shutdown hooks: 3
Number of shutdown hooks: 4
Number of shutdown hooks: 5
Number of shutdown hooks: 6
Number of shutdown hooks: 7
Number of shutdown hooks: 8
Number of shutdown hooks: 9
Number of shutdown hooks: 10
...

(Related to https://github.com/classgraph/classgraph/issues/233)

Issue Analytics

  • State:closed
  • Created 5 years ago
  • Comments:6 (6 by maintainers)

github_iconTop GitHub Comments

1reaction
seanfcommented, Aug 26, 2018

I don’t really trust WeakReferences either 😃, so I modified my code:

import java.lang.reflect.Field;
import java.util.Map;
import java.util.Set;

import io.github.classgraph.ClassGraph;
import io.github.classgraph.ScanResult;

public class ClassGraphShutdownHooks {
    public static void main(String[] args) throws Exception {
        for (int i = 0; i < 100_000; i++) {
//            try (ScanResult scan = new ClassGraph().scan()) {
//            }
            new ClassGraph().scan();
            System.out.printf("Number of references: %d%n", countRefs());
            System.out.printf("Number of shutdown hooks: %d%n", countShutdownHooks());
        }
    }

    // thanks to https://stackoverflow.com/a/6865509/14379
    // works on Java 8
    private static int countShutdownHooks() throws ReflectiveOperationException {
        Class clazz = Class.forName("java.lang.ApplicationShutdownHooks");
        Field field = clazz.getDeclaredField("hooks");
        field.setAccessible(true);
        Map<?, ?> hooks = (Map<?, ?>) field.get(null);
        return hooks.size();
    }

    private static int countRefs() throws ReflectiveOperationException {
        Field field = ScanResult.class.getDeclaredField("nonClosedWeakReferences");
        field.setAccessible(true);
        Set hooks = (Set) field.get(null);
        return hooks.size();
    }

}

Now if I use try-with-resources as recommended, references don’t accumulate at all (nor hooks!):

Number of references: 0
Number of shutdown hooks: 1
Number of references: 0
Number of shutdown hooks: 1
Number of references: 0
Number of shutdown hooks: 1
Number of references: 0
Number of shutdown hooks: 1
Number of references: 0
Number of shutdown hooks: 1
...

On the other hand, if I fail to close the ScanResults, the finalizer keeps the weak-refs down:

...
Number of references: 10
Number of shutdown hooks: 1
Number of references: 11
Number of shutdown hooks: 1
Number of references: 12
Number of shutdown hooks: 1
Number of references: 1
Number of shutdown hooks: 1
Number of references: 2
Number of shutdown hooks: 1
...

Ah, I get it. Didn’t notice the bit in close() which was calling nonClosedWeakReferences.remove(weakReference).

Excellent. Thanks @lukehutch!

1reaction
lukehutchcommented, Aug 26, 2018

Good to know. I have never needed them before, and there are tons of gotchas to using them right, so this has been a step learning curve. It was a good catch, thanks.

Read more comments on GitHub >

github_iconTop Results From Across the Web

shutdown hook causes classloader leak when library is used ...
Scanresult class adds a shutdown hook. This causes a classloader leak when tomcat unloads a webapplication that uses classgraph library.
Read more >
ClassGraph library causes large memory leak in Geode after ...
ScanResult must be closed to avoid memory and file leaks. ... shutdown hook causes classloader leak when library is used inside a servlet ......
Read more >
ClassLoader & memory leaks: a Java love story - Aloïs Micard
ApplicationShutdownHooks is used to register special code to be executed upon JVM shutdown, and since our JVM is not restarting in our case...
Read more >
classgraph/classgraph classgraph-4.2.10 on GitHub - NewReleases.io
This means you have to close ScanResult instances yourself in a try-with-resources block (or rely on the shutdown hook to close the ScanResult...
Read more >
Queries regarding Shutdown hook - Stack Overflow
1) You are correct. 2) The Java process' memory will be reclaimed, but you might want to do other cleanup, like delete some...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found