What's the Java equivalent of .net's GC.KeepAlive?
Asked Answered
E

2

10

.NET has a function called GC.KeepAlive(Object). Its sole purpose is to ensure the lifetime of the referenced object lasts until code flow reaches the call.

This is normally not necessary unless one is interoperating with native code.

I have a situation where I've got a graph of C++ objects accessed via JNI, where certain root objects need to be kept alive to keep the children alive. Both the root objects and the child objects have mirrors in JVM land. If the root object is collected and freed on the C++ side (by way of a SWIG-generated finalizer), however, the child objects will become invalid, since their C++ backing object will have been freed.

This can be solved by ensuring the local variables that root the object graph have a lifetime that exceeds the last use of a child object. So I need an idiomatic function that does nothing to an object, yet won't be optimized away or moved (e.g. hoisted out of a loop). That's what GC.KeepAlive(Object) does in .NET.

What is the approximate equivalent in Java?

PS: some possibly illustrative code:

class Parent {
    long ptr;
    void finalize() { free(ptr); }
    Child getChild() { return new Child(expensive_operation(ptr)); }
}

class Child {
    long ptr;
    void doStuff() { do_stuff(ptr); }
}

// BAD CODE with potential for SIGSEGV
for (Parent p : getParents()) {
    p.getChild().doStuff();
}

The trouble is that the GC freeing Parent p will free the memory allocated for Child while doStuff is executing. GC has been observed to do this in practice. A potential fix if GC.KeepAlive was available:

// BAD CODE with potential for SIGSEGV
for (Parent p : getParents()) {
    p.getChild().doStuff();
    GC.KeepAlive(p);
}

I could e.g. call toString on p, but I won't be doing anything with its output. I could poke p into an array temporarily, but how do I know the JVM won't discard the store? Etc.

Ervin answered 17/3, 2014 at 14:28 Comment(3)
I don't think there is one. Java's GC doesn't work on a timer, at least as far as I know, it traverses the object graph, usually called a "Mark and Sweep". I think you would simply retain a reference to the objects in question somewhere, and the GC would mark them as used and not sweep them up during the GC process.Avatar
Hold on; if the finalizer is called, it means the holder of this native resource is out of reach from your code, so it is normal that it is freed. You can avoid that if you keep a reference to it, say in a pool for instanceAndesite
Yes, keeping a reference to the parent (root) object(s) is what I want to do. Trouble is, this reference is local, and needs to stay where it is, even though I do nothing with the reference. I'm worried that optimization will throw away my do-nothing work when its only side-effect is disabling premature finalization.Ervin
L
2

I guess you could use JMH Blackhole for this. It was designed for ensuring that the reference doesn't get eliminated in benchmarks so it should work.

Basically it just compares the given object reference against a stored volatile reference and reassigns the later with some small and decreasing probability (storing is expensive so it gets minimized).

Lashonda answered 17/3, 2014 at 16:55 Comment(1)
I doubt I'm going to get a better answer at this stage, so I'll give you the tick.Ervin
D
1

Whenever the garbage collector is aggressive enough to claim the object while invoking a native method, and also in Java world little people seem to care to the point that either the problem doesn't exist or there's a lot bugged code around, this other SO answer seems to provide a reasonable alternative to use GC.KeepAlive(Object), that is by using non-static native JNI methods, reasonably preventing any possible garbage collection of the instance invoking these methods.

Date answered 1/6, 2019 at 12:20 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.