In fact, it was a bug last year, that is, there was an online java. lang. OutOfMemoryError: Direct buffer memory.
Reason: start with the recovery mechanism of Direct Memory:
Our system uses a nio-based message queue, while direct memory is placed out of the heap, and direct memory is referenced when the heap is placed. Normally, when young gc is used, the dead reference is recycled, and the direct memory region is recycled. However, if the memory reference is moved to the old gen without Full GC, direct memory will never be accumulated and released. Eventually cause direct memory OOM. In fact, this will not be a problem. The problem is that we have added-XX: + DisableExplicitGC to the JVM, and many recommended jvm tuning parameters. The real reason is that System. gc () is explicitly called during the space allocation process of DirectByteBuffer, in order to force useless DirectByteBuffer objects to release their associated native memory through Full GC.
When DirectByteBuffer is allocated, the java. nio. Bits. reserveMemory (size, cap) method is called:
| The code is as follows: |
Copy code |
Static void reserveMemory (long size, int cap ){ If (! MemoryLimitSet & VM. isBooted ()){ MaxMemory = VM. maxDirectMemory (); MemoryLimitSet = true; } // Optimist! If (tryReserveMemory (size, cap )){ Return; } Final JavaLangRefAccess jlra = SharedSecrets. getJavaLangRefAccess (); // Retry while helping enqueue pending Reference objects // Which primary des executing pending Cleaner (s) which primary des // Cleaner (s) that free direct buffer memory While (jlra. tryHandlePendingReference ()){ If (tryReserveMemory (size, cap )){ Return; } } // Trigger VM's Reference processing System. gc (); // A retry loop with exponential back-off delays // (This gives VM some time to do it's job) Boolean interrupted = false; Try { Long sleepTime = 1; Int sleeps = 0; While (true ){ If (tryReserveMemory (size, cap )){ Return; } If (sleeps> = MAX_SLEEPS ){ Break; } If (! Jlra. tryHandlePendingReference ()){ Try { Thread. sleep (sleepTime ); SleepTime <= 1; Sleeps ++; } Catch (InterruptedException e ){ Interrupted = true; } } } // No luck Throw new OutOfMemoryError ("Direct buffer memory "); } Finally { If (interrupted ){ // Don't swallow interrupts Thread. currentThread (). interrupt (); } } } |
In tryReserveMemory, determine whether the remaining capacity can be allocated with a specified size of direct memory. If yes, run System. gc () to recycle the direct memory, when the System. if gc () is disabled, throw new OutOfMemoryError occurs.
Solution:
Remove-XX: + DisableExplicitGC. However, frequent calling of System. gc in the nio framework seriously affects the performance. In fact, there is a better solution, using the-XX: + ExplicitGCInvokesConcurrent or-XX: + ExplicitGCInvokesConcurrentAndUnloadsClasses parameter. Like-XX: + DisableExplicitGC, these two parameters are also used to change the System. the default gc () behavior is used. The difference is that these two parameters can only be used with CMS (-XX: + UseConcMarkSweepGC), and System. gc () will still trigger GC, but not trigger a full GC of full stop-the-world, but a concurrent GC cycle (note: one concurrency cycle is actually one gc in CMS. CMS can only be used in full gc, so it is also a full gc, but it is more efficient ).
Summary:
When the program appears:
Using NIO or NIO frameworks (Mina/Netty, etc)
Uses DirectByteBuffer to allocate a byte buffer.
Memory ing using MappedByteBuffer
It is best to use the ExplicitGCInvokesConcurrent parameter to replace DisableExplicitGC. Note that ExplicitGCInvokesConcurrent can only be used with the CMS collector.