| I've done a fair bit of work interfacing memory managed languages like Java & Python with "unsafe" native code and found that this problem actually crops up often. The first issue is that finalizers should NEVER be relied upon to clean up system resources the way that you might in a language with deterministic garbage collection like C++. Instead, expect clients of interfaces that use system resources to manually call some 'close' like method. Use finalizers to warn clients that they are leaking. Josh Bloch has an excellent writeup on this[†]: http://www.informit.com/articles/article.aspx?p=1216151&seqN... The second thing, as I believe @btown is saying, is that the child object seems to depend on memory that it doesn't own a strong reference to. I bet the whole memory graph here is just a tangled mess. I used to think that that was just the nature of graphs. When I first started working with GC that couldn't handle these kind of cyclical structures without being explicitly told I thought that it was an absurd premature optimization. Then, this wise old hacker peered at my code, smacked me on the back of the head, and grumbled that if I couldn't clearly explain the reference hierarchy it wasn't just the software that would be befuddled, it was too complicated for his old brain as well. I'm sure that this can't be true, but I am starting to suspect that all the tremendous work that has gone into fast pause-less concurrent GC has been for naught. I wish that when GC encountered a cycle it would just tell the programmer that they now have a leak, maybe even suggest where it could be fixed with one of those neat little IDE quick fixes, and continue chugging along. tl;dr When you have a hard problem let someone else deal with it. [†] Granted, Bloch does make an exception for 'native peers'. My understanding of that pattern, however, is that the key is not letting some other object walk off with a reference to your peer, which is exactly what has gone wrong here. (Aside: Josh Bloch's Effective Java & Java Puzzlers are fantastic. They are worth reading for programmers who use any language.) |
Exactly. Having made similar types of robotics interfaces myself, the memory graph for OP probably isn't as complicated/cyclic as you might think: multiple types of frames (at various stages of visualization processing) look into each others' memory buffers for efficiency. It's probably best modeled as an acyclic directed graph where frame objects reference (many-to-many) memory buffers. The problem is that unless you abstract out those buffers into garbage-collectible objects themselves, it's very tempting just to pass a weak reference (like a pointer from JNI) to the buffer owned by an EarlyStageFrame to the processing code in a LaterStageFrame. Thus the segfault when the EarlyStageFrame is garbage-collected.