Java direct memory: using sun.misc.Cleaner in custom classes
Asked Answered
N

3

15

In Java the memory allocated by NIO direct buffers is freed with sun.misc.Cleaner instances, some special phantom references that are more efficient than object finalization.

Is this cleaner mechanism hardcoded in the JVM only for the direct buffer subclasses, or is it possible to also use cleaners in custom components (writing a custom direct byte buffer for instance)?

Here I am not talking about retrieving the cleaner field of an existing nio direct buffer. I am not talking either about manually freeing the memory. This is about writing a new class that allocates direct memory and has it cleaned efficiently and automatically by the garbage collector mechanism.

Needlecraft answered 16/7, 2013 at 7:58 Comment(3)
You shouldn't use anything in sun.misc.* for anything whatsoever, and the JDK documentation already says so.Zarah
I know, and also I should not smoke ;) It is a good thing that the writers of high performance libraries based on sun.misc.Unsafe bypassed that rule though.Needlecraft
Antoine, it's avoidable. Only sun.misc.Cleaner will survive, look at the JEP 260 and test your code with Java 1.9, you'll see what we mean. My deallocator helper supports Java 1.9 and it doesn't use sun.misc classes except sun.misc.Cleaner.Reservation
N
18

After spending more time reading the API doc ( http://docs.oracle.com/javase/7/docs/api/java/lang/ref/package-summary.html ) I think I have a more detailed answer:

1) it is possible to reuse sun.misc.Cleaner to perform the efficient cleanup of your own custom classes. You declare the cleaner by calling the provided factory method:

sun.misc.Cleaner.create(Object ob, Runnable cleanup);

For some time I could not get it to work properly, that's because I was moronic enough to define the runnable cleanup code of my cleaner as an anonymous class, that kept a (strong) reference to my referent object, preventing it from ever being "phantom reachable"...

2) There is no other way to implement such efficient cleanup (not even with the help of phantom references)

Indeed the reference handler thread handles instances of sun.misc.Cleaner in a special way:

// Fast path for cleaners
if (r instanceof Cleaner) {
    ((Cleaner)r).clean();
    continue;
}

That means that the cleanup code is called directly from the reference handler thread, while in standard usage, the references must be enqueued by the reference handler thread and then dequeued and processed by another application thread.

Needlecraft answered 17/7, 2013 at 12:0 Comment(1)
By the way, in Java 9 there is a java.lang.ref.Cleaner which is a standard replacement for sun.misc.CleanerCordeiro
G
2

If you rely on anything in a sun.misc package, you run the risk of it disappearing and breaking your code. Some pieces are more stable than others, but it's often a bad idea (devil's advocate: a lot of the methods in sun.misc.Unsafe are actually implemented by JVM intrinsics, making them faster than user-written JNI code).

In this case, I think it's a bad idea: Cleaner is one possible implementation of cleanup via PhantomReference. There are others; Google for examples. For that matter, you could look at the source code of Cleaner itself as an example of how to use phantom references.

You will need some sort of cleanup handler if you're going to have on-heap objects that refer to off-heap objects. Otherwise you'll create a true memory leak when those on-heap objects get collected.

Glioma answered 16/7, 2013 at 14:36 Comment(0)
S
2

Hope it help you if using java9.

The code below was already tested in Intellij IDEA 2017 and oracle jdk 9.

import java.lang.ref.Cleaner;

public class Main {

    public Main() {

    }

    public static void main(String[] args) {
        System.out.println("Hello World!");

        while (true) {
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            Cleaner cleaner = Cleaner.create();
            Main obj = new Main();
            cleaner.register(obj, new Runnable() {
                @Override
                public void run() {
                    System.out.println("Hello World!222");
                }
            });
            System.gc();
        }
    }
}
Scholem answered 19/4, 2018 at 3:29 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.