Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Is there a need to free memory of Unpooled.unreleasableBuffer #8139

Closed
felixunivers opened this issue Jul 18, 2018 · 2 comments
Closed

Is there a need to free memory of Unpooled.unreleasableBuffer #8139

felixunivers opened this issue Jul 18, 2018 · 2 comments

Comments

@felixunivers
Copy link

Say, we declare in MyClass:

ByteBuf fixedBuf = Unpooled.unreleasableBuffer(PooledByteBufAllocator.DEFAULT.directBuffer(64, 64));

This should reserve a 64 bytes chunk of off-heap memory. As documented, Netty ignores here the 'release()' and 'retain()' functionality, therefore, the chunk remains allocated throughout the life of MyClass. What will happen to the allocated off-heap memory chunk once the instance of MyClass gets out of scope? Will JVM/GC release it? If not, or if I want to discard the 'fixedBuf ' programatically, how can I best deallocate the claimed memory blocks for such ByteBuf objects?

Would you please also point to explanation(s) / example(s) / best practice for avoiding any memory leaks and issues on ByteBuf objects?

@Mr00Anderson
Copy link
Contributor

Mr00Anderson commented Jul 24, 2018

(JVM only) It appears that the Direct Buffer will be cleaned up if no references are to it by default in the JVM during the heap GC but not effected by heap pressure so manual cleaning may be required as per previous stack overflow questions. Stack Overflow 1, Stack Overflow 2

(For Netty) Since Netty references the Direct Buffer I would think that it does not clean it until the PoolThreadCache runs the method free() which manually uses the Netty Internal/Cleaners which happens in finalize block of the PoolThreadCache.java and a other locations. If you are going out of scope it should clean automatically when the PoolThreadCache.java uses free; but I have not tracked down when that happens. If in doubt use Unpooled.unwrap() and release it yourself.

@trustin or @normanmaurer just to verify this is correct. (Hope so, spent 1 or 2 hours navigating the code)

Working notes from browsing the class files below.

Reading through the Unpooled.unreasableBuffer(ByteBuf byteBuff) it just wraps it in a UnreleasableByteBuf class which is just a WrappedByteBuf. The behavior would be in the cleaners and original allocation in PooledByteBufAllocator. Which by default uses public static final PooledByteBufAllocator DEFAULT = new PooledByteBufAllocator(PlatformDependent.directBufferPreferred()); which ends up down the line using

@Override
    protected ByteBuf newDirectBuffer(int initialCapacity, int maxCapacity) {
        PoolThreadCache cache = threadCache.get();
        PoolArena<ByteBuffer> directArena = cache.directArena;

        final ByteBuf buf;
        if (directArena != null) {
            buf = directArena.allocate(cache, initialCapacity, maxCapacity);
        } else {
            buf = PlatformDependent.hasUnsafe() ?
                    UnsafeByteBufUtil.newUnsafeDirectByteBuf(this, initialCapacity, maxCapacity) :
                    new UnpooledDirectByteBuf(this, initialCapacity, maxCapacity);
        }

        return toLeakAwareBuffer(buf);
    }

leading us to ...
private final PoolArena<ByteBuffer>[] directArenas; leading to PooledArena static class static final class DirectArena extends PoolArena<ByteBuffer> and in it

private static ByteBuffer allocateDirect(int capacity) {
            return PlatformDependent.useDirectBufferNoCleaner() ?
                    PlatformDependent.allocateDirectNoCleaner(capacity) : ByteBuffer.allocateDirect(capacity);
        }

and inside PoolArena void free(PoolChunk<T> chunk, long handle, int normCapacity, PoolThreadCache cache) which calls

        protected void destroyChunk(PoolChunk<ByteBuffer> chunk) {
            if (PlatformDependent.useDirectBufferNoCleaner()) {
                PlatformDependent.freeDirectNoCleaner(chunk.memory);
            } else {
                PlatformDependent.freeDirectBuffer(chunk.memory);
            }
        }

which looks like ultimately it depends on the thread PoolThreadCache.java which is what calls

 /**
     *  Should be called if the Thread that uses this cache is about to exist to release resources out of the cache
     */
    void free() {
        // As free() may be called either by the finalizer or by FastThreadLocal.onRemoval(...) we need to ensure
		// we only call this one time.

when it frees it destroys the chunk

        @Override
        protected void destroyChunk(PoolChunk<ByteBuffer> chunk) {
            if (PlatformDependent.useDirectBufferNoCleaner()) {
                PlatformDependent.freeDirectNoCleaner(chunk.memory);
            } else {
                PlatformDependent.freeDirectBuffer(chunk.memory);
            }
        }

@normanmaurer
Copy link
Member

@felixunivers to release such a buffer you would need to unwrap it first. And yes you definitely want to do this as otherwise you will leak it.

So something like:

unreleaseableBuffer.unwrap().release()

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants