Thanks for sharing the information Laurent. I'd think JDK will always provide options to leverage garbage collector when it comes to cleaning up external resources (like native memory) binding to a object. In panama, as you can see, the new MemorySegment interfaces still allow users to register GC-hooks to do cleanup. See "Implicit deallocation" chapter in this documentation[1].
By the way, in Arrow things can be a little bit more complicated, since the allocations has a local scope which is BufferAllocator, but JDK always uses global direct memory scope, even in panama. Hongze [1] https://docs.oracle.com/en/java/javase/16/docs/api/jdk.incubator.foreign/jdk/incubator/foreign/MemorySegment.html On Thu, 2021-10-07 at 21:40 -0700, Laurent Goujon wrote: > Worth also observing that the Java community has been recognizant of the > issues with the current approach around direct byte buffer and GC, and has > come up with a proposal/implementation to avoid interaction with GC and > give more direct control. The proposal (actually, its 3rd iteration) is > described here at https://openjdk.java.net/jeps/393, and has been available > as an incubator feature for several JDK releases (Javadoc: > https://docs.oracle.com/en/java/javase/17/docs/api/jdk.incubator.foreign/jdk/incubator/foreign/package-summary.html > ) > > Laurent > > > On Thu, Oct 7, 2021 at 4:40 PM Jacques Nadeau <jacq...@apache.org> wrote: > > > Clearly this patch was driven by an implicit set of needs but it's hard to > > guess at what they are. As Laurent asked, what is the main goal here? There > > may be many ways to solve this goal. > > > > Some thoughts in general: > > - The allocator is a finely tuned piece of multithreaded machinery that is > > used on hundreds of thousands of vm's ever year. As such, refactors should > > be avoided without a clear pressing need. When a need exists, it should be > > discussed extensively and thoroughly designed/vetted before proposing any > > patches. > > - Having used the library extensively, I can state that the library is > > already too heap impacting given the nature of the objects. Increasing GC > > load/reliance is actually in direct conflict to many of the past > > optimizations of the allocator and vector objects. > > - As far as I can tell, a new allocation manager could do auto-releases. > > Why go beyond introducing a new allocation manager (or even a backstopping > > allocation manager facade). The allocation manager was actually built to > > solve for things like memory moving between C and java. > > - It isn't clear here how much this is proposed as a backstop mechanism > > versus a primary release mechanism. I'm more in support of an optional > > backstop mechanism so that production apps don't leak (with appropriate > > logged warnings, etc). > > - Relying on GC to release direct memory frequently leads to early OOMs > > (exactly why the release mechanism was in place and is chosen for high perf > > apps like Netty and Arrow). It is fairly easy to allocate a bunch of direct > > memory but the GC never kicks in because heap memory is not under load. > > > > Lastly, I agree with Micah on the library use confusion. If we have close() > > methods everywhere and they javadoc they are required to release memory but > > then we implement something that makes that possibly not true (or not true > > in some contexts), wowser on the confusion. > > > > > > > > > > On Thu, Oct 7, 2021 at 3:55 PM Micah Kornfield <emkornfi...@gmail.com> > > wrote: > > > > > In addition to the points raised by Laurent, I'm concerned about having > > two > > > possible idioms in the same library. It means code written against the > > > library becomes less portable (you need to know how the memory allocator > > is > > > using GC or not). > > > > > > I understand manual memory management in Java is tedious but is there a > > > specific problem this is addressing other than making Arrow have more > > > expected semantics to Java users? Are there maybe alternative solutions > > to > > > addressing the problem? > > > > > > > > > -Micah > > > > > > > > > > > > On Thu, Oct 7, 2021 at 3:48 PM Hongze Zhang <notify...@126.com> wrote: > > > > > > > We don't have to concern about that since no difference will be made on > > > > current manual release path unless "MemoryChunkCleaner" is explicitly > > > > specified when creating BufferAllocators. Manual ref counting will be > > > only > > > > discard/ignored in "MemoryChunkCleaner", while by default > > > > "MemoryChunkManager" is used which is totally the same as current. > > > > > > > > > > > > > > > > > > > > For example, to enable GC-based functionalities, the BufferAllocator > > has > > > > to be created using the following way: > > > > > > > > > > > > > > > > > > > > BufferAllocator root = new RootAllocator( > > > > > > > > BaseAllocator.configBuilder() > > > > > > > > .maxAllocation(MAX_ALLOCATION) > > > > > > > > .memoryChunkManagerFactory(MemoryChunkCleaner.newFactory()) > > > // > > > > to enable GC-based buffer release > > > > > > > > .listener(MemoryChunkCleaner.gcTrigger()) // to trigger JVM > > > GC > > > > when allocator is full > > > > > > > > .build()); > > > > > > > > Thanks, > > > > > > > > Hongze > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > At 2021-10-07 12:53:23, "Laurent Goujon" <laur...@dremio.com> wrote: > > > > > Unless I missed something in the big picture, but it seems that using > > a > > > GC > > > > > based approach would make things much inefficient in terms of memory > > > > > management and would cause probably quite the confusion for all the > > > > systems > > > > > out there which rely on refcounting for keeping things in check, I'm > > not > > > > > sure why changing the default is such a good idea... > > > > > > > > > > On Tue, Oct 5, 2021 at 2:20 AM Hongze Zhang <notify...@126.com> > > wrote: > > > > > > > > > > > Hi Laurent, > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > Sorry I might describe it unclearly and yes, the purpose is > > > > > > straightforward: to discard (in another strategy rather than > > default) > > > > the > > > > > > usage of Java buffer ref counting and rely on GC to do cleanup for > > the > > > > > > buffers. Though some design changes to Allocator APIs maybe required > > > to > > > > > > achieve this, for example, the decoupling of MemoryChunkAllocator > > from > > > > > > AllocationManager. > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > Should we make more discussion on it, or I can open a ticket asap? > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > Thanks, > > > > > > > > > > > > Hongze > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > At 2021-10-05 13:39:55, "Laurent Goujon" <laur...@dremio.com> > > wrote: > > > > > > > Hi, > > > > > > > > > > > > > > I gave a quick look at your patches but to be honest, it's not > > > > > > > easy > > > to > > > > > > > figure out the problem you're trying to solve in the first place. > > Or > > > > is it > > > > > > > simply to discard the use of ref counting to track buffer usages > > and > > > > > > > relying on Java references to keep track of buffers at the expense > > of > > > > > > > creating more pressure on the GC itself to collect and free > > buffers? > > > > > > > > > > > > > > On Wed, Sep 29, 2021 at 11:58 PM Hongze Zhang <notify...@126.com> > > > > wrote: > > > > > > > > > > > > > > > Hi, > > > > > > > > > > > > > > > > I would like to discuss on the potential of introducing a > > GC-based > > > > > > > > reference management strategy to Arrow Java, and we > > > > > > > > have already been working on an implementation in our own > > project. > > > I > > > > > > have > > > > > > > > put the related codes in following branch and > > > > > > > > if it makes sense to upstream Apache Arrow I can open a PR for > > it: > > > > > > > > > > > > > > > > > > > > > > > > > > https://github.com/zhztheplayer/arrow-1/commits/wip-chunk-cleaner > > > > > > > > > > > > > > > > In the branch, regarding Commit 1, Commit 2 and Commit 3: > > > > > > > > > > > > > > > > Commit 1: To break AllocationManager to two components: > > > > > > > > MemoryChunkManager[1] which maintains BufferLedger- > > > > > > > > BufferAllocator mappings, and MemoryChunkAllocator[2] which > > > performs > > > > the > > > > > > > > underlying allocate/destory operations. The > > > > > > > > previous customizations such as NettyAllocationManager, > > > > > > > > UnsafeAllocationManager, are moved to MemoryChunkAllocator API, > > > > > > > > as NettyMemoryChunkAllocator and UnsafeMemoryChunkAllocator. > > > > > > > > > > > > > > > > Commit 2: To introduce a new implementation of > > MemoryChunkManager, > > > > > > > > MemoryChunkCleaner[3]. By default, MemoryChunkCleaner > > > > > > > > converts all managed chunks to WeakReferences, and a global > > thread > > > > will > > > > > > > > observe on the assigned reference queue to > > > > > > > > release the unused garbage chunks which are enqueued by JVM GC. > > > Some > > > > > > > > modes[4] are there to provide different strategies, > > > > > > > > such as to disable manual reference management (default), or to > > > > enable > > > > > > > > manual and GC-based reference management at the > > > > > > > > same time, or to report leaks only. > > > > > > > > > > > > > > > > Commit 3: Add API "ArrowBuf buffer(MemoryChunk chunk)" to > > > > > > BufferAllocator. > > > > > > > > This makes some special workloads, e.g. > > > > > > > > cross-JNI buffer sharing much easier as we will no longer need > > > > > > > > an > > > > > > > > Allocator directly binding to the shared memory > > > > > > > > chunks, and will still be able to leverage all of Allocator's > > > > advantages > > > > > > > > to manage the chunks (E.g. to use > > > > > > > > MemoryChunkCleaner). > > > > > > > > > > > > > > > > Some possible Q&As about this implementation: > > > > > > > > > > > > > > > > 1. Why adding MemoryChunkAllocator, rather than just customizing > > > > > > > > AllocationManager? > > > > > > > > It is to reuse all of current underlying chunk allocation > > > strategies, > > > > > > > > Netty, Unsafe, and others. Also, with the layer of > > > > > > > > MemoryChunk separated from AllocationManager we will be able to > > > > create > > > > > > > > MemoryChunks actively in some special cases, e.g. > > > > > > > > cross-JNI buffer sharing, C data interface buffer importing, > > > > > > > > then > > > > > > populate > > > > > > > > the chunks to a manager BufferAllocator. > > > > > > > > > > > > > > > > 2. Why using WeakReference rather than PhantomReference? > > > > > > > > In Java 8, PhantomReference has some issue against its referent > > > > object. > > > > > > > > The object cannot be garbage collected before > > > > > > > > being enqueued. In WeakReference we don't have this issue. See > > > > ref[5]. > > > > > > > > > > > > > > > > 3. Why not sun.misc.Cleaner? > > > > > > > > Cleaner API maintains a global doubly linked list to keep the > > > Cleaner > > > > > > > > instances alive. This brings overheads to us since > > > > > > > > we will create local doubly linked list for cleaning up all the > > > > buffers > > > > > > on > > > > > > > > Allocator close. See a unit test[6]. > > > > > > > > > > > > > > > > 4. Why closing all buffers on Allocator close? > > > > > > > > This behavior can be customizabale within Mode[7]s. A principle > > is, > > > > when > > > > > > > > relying on GC, we should allow closing buffers > > > > > > > > manually, or at least closing all of them on Allocator close. Or > > > the > > > > > > > > actual release time of the underlying chunks will > > > > > > > > be unpredictable. > > > > > > > > > > > > > > > > 5. Can we use heap based buffers? > > > > > > > > If I am not wrong, no. The heap objects can be physically moved > > > > around > > > > > > by > > > > > > > > JVM. The addresses can vary. > > > > > > > > > > > > > > > > 6. When should GC happen? > > > > > > > > A new AllocationListener implementation, GCTriger[8] is > > introduced. > > > > GC > > > > > > > > will be performed when BufferAllocator is full. > > > > > > > > > > > > > > > > 7. Performance? > > > > > > > > Based on previous measurement, it doesn't bring overheads on the > > > > legacy > > > > > > > > path (by using default MemoryChunkManager). For > > > > > > > > GC-based path (MemoryChunkCleaner + Mode.GC_ONLY), allocations > > can > > > be > > > > > > > > comparatively slower due to higher GC pressure > > > > > > > > (For example, in out Arrow-based query engine, to run TPC-DS > > > SF1000, > > > > the > > > > > > > > overhead can be up to 3%). I can collect more > > > > > > > > benchmark results in future, maybe under a JIRA ticket or a PR. > > > > > > > > > > > > > > > > 8. Breaking changes? > > > > > > > > It doesn't break library-based allocation strategy selection > > > > > > > > like > > > > > > directly > > > > > > > > including arrow-memory-unsafe.jar to > > > > > > > > projects. However it breaks use of the previous > > > > > > AllocationManager.Factory > > > > > > > > API. Users should migrate to > > > > > > > > MemoryChunkAllocator API. Which in my opinion is simple to do. > > > > > > > > > > > > > > > > The implementation is still evolving, so if the links get > > > out-of-date > > > > > > you > > > > > > > > can check on the newest branch head. Any > > > > > > > > suggestions welcome. > > > > > > > > > > > > > > > > Some other discussions that may be related to this topic: > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > https://lists.apache.org/thread.html/r09b2e7dd8e342818190c332ee20d97f68816241d0c683e17c4f35dc6%40%3Cdev.arrow.apache.org%3E > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > https://lists.apache.org/thread.html/rc956b16b7ed9768cce8eaecafdc363f7b3808dafe6edf1940b139ecb%40%3Cuser.arrow.apache.org%3E > > > > > > > > > > > > > > > > Best, > > > > > > > > Hongze > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > [1] > > > > > > > > > > > > > > > > > > > > > > > https://github.com/zhztheplayer/arrow-1/blob/52e5def7a239164b5c13457c8be38e227bb2907d/java/memory/memory-core/src/main/java/org/apache/arrow/memory/ > > > > > > > > MemoryChunkManager.java > > > > > > > > [2] > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > https://github.com/zhztheplayer/arrow-1/blob/52e5def7a239164b5c13457c8be38e227bb2907d/java/memory/memory-core/src/main/java/org/apache/arrow/memory/MemoryChunkAllocator.java > > > > > > > > > > > > > > > > [3] > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > https://github.com/zhztheplayer/arrow-1/blob/52e5def7a239164b5c13457c8be38e227bb2907d/java/memory/memory-core/src/main/java/org/apache/arrow/memory/MemoryChunkCleaner.java > > > > > > > > [4] > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > https://github.com/zhztheplayer/arrow-1/blob/52e5def7a239164b5c13457c8be38e227bb2907d/java/memory/memory-core/src/main/java/org/apache/arrow/memory/MemoryChunkCleaner.java#L503 > > > > > > > > [5] > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > https://stackoverflow.com/questions/56684150/why-since-java-9-phantomreference-java-doc-states-that-it-is-dedicated-to-the-po > > > > > > > > [6] > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > https://github.com/zhztheplayer/arrow-1/blob/52e5def7a239164b5c13457c8be38e227bb2907d/java/memory/memory-core/src/test/java/org/apache/arrow/memory/TestMemoryChunkCleaner.java#L204 > > > > > > > > [7] > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > https://github.com/zhztheplayer/arrow-1/blob/52e5def7a239164b5c13457c8be38e227bb2907d/java/memory/memory-core/src/main/java/org/apache/arrow/memory/MemoryChunkCleaner.java#L503 > > > > > > > > [8] > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > https://github.com/zhztheplayer/arrow-1/blob/52e5def7a239164b5c13457c8be38e227bb2907d/java/memory/memory-core/src/main/java/org/apache/arrow/memory/MemoryChunkCleaner.java#L402 > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > >