question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

Direct Buffer Used Despite Parameters Indicating Not To

See original GitHub issue

Expected behavior

If -Dio.netty.allocator.numDirectArenas=0 and -Dio.netty.noPreferDirect=true, that Direct Buffers are not used when a Heap Buffer is possible.

Actual behavior

Instead, Direct Buffers are always used as shown in this Stacktrace:

16:37:49.059 [nioEventLoopGroup-3-4] WARN  DefaultChannelPipeline - An exceptionCaught() event was fired, and it reached at the tail of the pipeline. It usually means the last handler in the pipeline did not handle the exception.
io.netty.util.internal.OutOfDirectMemoryError: failed to allocate 480 byte(s) of direct memory (used: 1073741767, max: 1073741824)
	at io.netty.util.internal.PlatformDependent.incrementMemoryCounter(PlatformDependent.java:535)
	at io.netty.util.internal.PlatformDependent.allocateDirectNoCleaner(PlatformDependent.java:489)
	at io.netty.buffer.UnpooledUnsafeNoCleanerDirectByteBuf.allocateDirect(UnpooledUnsafeNoCleanerDirectByteBuf.java:30)
	at io.netty.buffer.UnpooledUnsafeDirectByteBuf.<init>(UnpooledUnsafeDirectByteBuf.java:67)
	at io.netty.buffer.UnpooledUnsafeNoCleanerDirectByteBuf.<init>(UnpooledUnsafeNoCleanerDirectByteBuf.java:25)
	at io.netty.buffer.UnsafeByteBufUtil.newUnsafeDirectByteBuf(UnsafeByteBufUtil.java:410)
	at io.netty.buffer.PooledByteBufAllocator.newDirectBuffer(PooledByteBufAllocator.java:336)
	at io.netty.buffer.AbstractByteBufAllocator.directBuffer(AbstractByteBufAllocator.java:183)
	at io.netty.buffer.AbstractByteBufAllocator.directBuffer(AbstractByteBufAllocator.java:174)
	at io.netty.buffer.AbstractByteBufAllocator.ioBuffer(AbstractByteBufAllocator.java:135)
	at io.netty.channel.AdaptiveRecvByteBufAllocator$HandleImpl.allocate(AdaptiveRecvByteBufAllocator.java:104)
	at io.netty.channel.nio.AbstractNioByteChannel$NioByteUnsafe.read(AbstractNioByteChannel.java:117)
	at io.netty.channel.nio.NioEventLoop.processSelectedKey(NioEventLoop.java:646)
	at io.netty.channel.nio.NioEventLoop.processSelectedKeysOptimized(NioEventLoop.java:581)
	at io.netty.channel.nio.NioEventLoop.processSelectedKeys(NioEventLoop.java:498)
	at io.netty.channel.nio.NioEventLoop.run(NioEventLoop.java:460)
	at io.netty.util.concurrent.SingleThreadEventExecutor$2.run(SingleThreadEventExecutor.java:131)
	at io.netty.util.concurrent.FastThreadLocalRunnable.run(FastThreadLocalRunnable.java:30)
	at java.lang.Thread.run(Thread.java:748)

Why? See io.netty.buffer.AbstractByteBufAllocator.ioBuffer(AbstractByteBufAllocator.java:135) aka https://github.com/netty/netty/blob/2dc686ded17d8123118921ac6d36aea30cdce267/buffer/src/main/java/io/netty/buffer/AbstractByteBufAllocator.java#L128-L150

 @Override
    public ByteBuf ioBuffer() {
        if (PlatformDependent.hasUnsafe() || isDirectBufferPooled()) {
            return directBuffer(DEFAULT_INITIAL_CAPACITY);
        }
        return heapBuffer(DEFAULT_INITIAL_CAPACITY);
    }

From the above, if hasUnsafe evaluates to true, directBuffer() is invoked vs. heapBuffer().

The only workaround is to provide -Dio.netty.noUnsafe=true in addition to the aforementioned parameters.

Steps to reproduce

N/A

Minimal yet complete reproducer code (or URL to code)

N/A

Netty version

Latest

JVM version (e.g. java -version)

openjdk version “1.8.0_202”

OS version (e.g. uname -a)

Ubuntu 16.04

Issue Analytics

  • State:open
  • Created 4 years ago
  • Comments:6 (4 by maintainers)

github_iconTop GitHub Comments

4reactions
amcrncommented, Nov 4, 2019

Thanks for the quick replies @ejona86 @carl-mastrangelo @normanmaurer , I really appreciate it.

Can you explain your usage pattern more? I.e. why can you not pass in PreferHeapByteBufAllocator into the Channel config?

(Note: I’m going to be very explicit with flags and such so that other people with the same issue can find it in a Google search)

My attempt to avoid direct buffers arose out of trying to triage OutOfDirectMemoryErrors being thrown repeatedly in a high throughput application. Turning on -Dio.netty.leakDetection.level=paranoid returned nothing, and limiting -XX:MaxDirectMemorySize didn’t prove fruitful either. Inspecting direct memory is fairly difficult in our deployment environment, so I was looking to have all allocations heap-based when possible, that way an OutOfMemoryError would trigger a Heap Dump via -XX:+HeapDumpOnOutOfMemoryError, and then I could begin to investigate what was “leaking” or dominating.

The following three parameters were enough to cause a Heap Dump vs. OutOfDirectMemoryError: -Dio.netty.allocator.numDirectArenas=0, -Dio.netty.noPreferDirect=true, and -Dio.netty.noUnsafe=true. After analyzing the Heap Dump, I was able to determine that reducing -Dio.netty.allocator.maxOrder to 3 for our Heap size (~2GB) was the appropriate solution, as that in turn reduced the chunkSize (and other artifacts) to a more reasonable limit.

Long story short: Having the option to use heap memory vs. direct memory proved very useful in identifying a “leak” (in this case, it wasn’t really a leak, just too aggressive of an allocation strategy) in a deployment environment that doesn’t permit direct access to the Box/VM/container.

I think “don’t use any direct buffers” is more of a feature request

Fair enough. Maybe a small enhancement could be made to the documentation or Javadoc indicating that even if -Dio.netty.allocator.numDirectArenas=0 and -Dio.netty.noPreferDirect=true, direct memory will still be used (and why).

Can you explain your usage pattern more? I.e. why can you not pass in PreferHeapByteBufAllocator into the Channel config?

Good question. Netty in this case is being used by a third party library that we have no control over. Is there a -Dio.netty flag that can be set to indicate the Class desired?

1reaction
ejona86commented, Oct 31, 2019

I don’t think those configuration options imply “no direct buffers.” They mean “no pooling for direct buffers” and “given a choice between direct and heap, whether direct should be used.”

I think “don’t use any direct buffers” is more of a feature request. Right now any code that calls alloc.directBuffer() should get a direct buffer. And it seems fair that ioBuffer also is a direct buffer, given how memory works in Java. So I don’t know if even that makes sense.

@amcrn, can you explain why direct memory is off-limits for you?

Read more comments on GitHub >

github_iconTop Results From Across the Web

How to garbage collect a direct buffer in Java - Stack Overflow
A direct byte buffer may be created by invoking the allocateDirect factory method of this class. The buffers returned by this method typically ......
Read more >
Troubleshooting OutOfMemoryError: Direct buffer memory
OutOfMemoryError: Direct buffer memory Error will be throw. A good runtime indicator of a growing Direct Buffers allocation is the size of Non- ......
Read more >
Direct Buffer - Aeron Cookbook
DirectBuffer implementations offer methods to read and write both individual bytes along with regions of bytes. Char data is also supported, although it...
Read more >
OBJ53-J. Do not use direct buffers for short-lived, infrequently ...
Direct buffers are also outside the scope of Java's garbage collector; consequently, injudicious use of direct buffers can cause memory leaks.
Read more >
Buffers | Calbiochem
Table 1: pKa Values for Commonly Used Biological Buffers and Buffer Constituents. Product. Cat. No. M.W.. pKa at. 20°C. BES, ULTROL® Grade.
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found