GC & native objects

8 views
Skip to first unread message

Terasgr

unread,
Jul 7, 2018, 8:58:06 AM7/7/18
to Avian
I have a second question, in order for Avian to be my dream implementation.
it has to do with memory management (we have discussed it in the past).
But at that time I fell into the pit of the XY problem.

Some background info first:
I want in Avian to handle native (Objective C) objects. This means that a natively created ObjC object is passed to Avian runtime (as a "long" peer pointer), and brought around like this. I am perfectly capable of manipulating it, calling its methods, communicate forward and backwards with java - everything works. Just to make sure that ObjC won't destroy it while there is a pointer in Java, I increase the counting by one, when Java requests this object for the first time. The object could be created either by Java itself or by the system and mapped to a Java object.

The problem is with memory management - aka when the object dies. 
I can not rely on GC, because GC doesn't have any knowledge of the retain count, and it is possible an object to GC'ed in Java, still be kept in native runtime and re-requested again in Java.
And I can not just rely on ObjC, and check when the retain count drops to 1 (this means only java has a reference), since there is no concrete method to be notified when this counter == 1.  Apple has already done this trick for some internal processing of its own and it would be very tricky to support it for every possible object available - by breaking its own API.


Thus, I'd like to beg you here your thoughts about it. How it could be to safely handle this situations?

Joel Dice

unread,
Jul 9, 2018, 10:35:54 AM7/9/18
to Avian
I don't know of any garbage-collected language that interoperates well
with reference-counted and manually-managed memory. In my opinion,
the best option is to wrap pointers to native resources in a class
like this:

public class Foo implements AutoCloseable {
private long peer;

public Foo() {
this.peer = createFoo();
}

public synchronized void doSomething() {
if (peer == 0) {
throw new IllegalStateException("already closed");
}

nativeDoSomething(peer);
}

public synchronized void close() {
try {
releaseFoo(peer);
} finally {
peer = 0;
}
}

private static native long createFoo();

private static native void nativeDoSomething(long peer);

private static native void releaseFoo(long peer); // decrement ref
count or whatever is appropriate
}

That allows you to safely release the resource when you're done using
it and avoid dangling pointers. You can use try-with-resources to
ensure a resource with a scoped lifetime is always released. If the
lifetime is more complicated, you have to explicitly call `close` when
the resource is no longer needed. That's how the RocksDB Java
bindings work: https://github.com/facebook/rocksdb/wiki/RocksJava-Basics#memory-management.
A similar strategy is used for other resources in Java like network
sockets, etc. which need to be managed independently of garbage
collected memory.

It's annoying to have to manually manage resources like this, but it's
the only efficient way to do it in a language like Java. Rust and C++
have smart pointers which make this quite a bit easier, and that's one
of the reasons I prefer Rust over Java these days.
> --
> You received this message because you are subscribed to the Google Groups
> "Avian" group.
> To unsubscribe from this group and stop receiving emails from it, send an
> email to avian+un...@googlegroups.com.
> To post to this group, send email to av...@googlegroups.com.
> Visit this group at https://groups.google.com/group/avian.
> For more options, visit https://groups.google.com/d/optout.

Terasgr

unread,
Jul 11, 2018, 3:30:17 AM7/11/18
to Avian
Thank you for your reply

Unfortunately  the solution you provide, although the most logical one, is out of the scope of the question, which probably I didn't express it correctly.
The idea is to map any random ObjC object and for all of them have the equivalent Java classes. Not just a couple of objects but the whole API.

After some draft discussions with the guys at RoboVM, they gave me a hint. I don't need to keep track of all ObjC objects, as it was my original idea, because I'd have to override the release selector of all objects through swizzling (which is dangerous as I mentioned). It is enough to just override release of only the java overridden (through inheritance) objects, which is safe. For all other not-overridden objects let them die in peace and re-create them when needed, since there is no special java-related information in them.
Reply all
Reply to author
Forward
0 new messages