An overview of the

Generalized off-heap memory

When we talk about out-of-heap memory, we think about in-heap memory, and that’s what we’re dealing with most. We usually set -xmx in JVM parameters to specify the maximum size of our heap, but that’s not what we think of as Java heap, -xmx is the maximum value of the sum of the new generation and the old generation, -xx :MaxPermSize = -xmx + -xx :MaxPermSize = -xx :MaxPermSize = -xmx + -xx :MaxPermSize Since they are allocated together, the rest can be considered out-of-heap memory (generalized). This includes memory allocated by the JVM itself during runtime, codecache, JNI, DirectByteBuffer, and so on

Narrow sense of out-of-heap memory

As a Java developer, we often say of heap memory, is actually a special heap memory, this mainly refers to the Java nio. DirectByteBuffer allocated memory when creating, we of this article is mainly about special heap memory, because it is close and we usually run into problems

JDK/JVM DirectByteBuffer implementation

DirectByteBuffer is a buffer pool used in mina, Netty and other NIO frameworks.

As we know from the constructor above, the real memory allocation is using the bits.Reservememory method

We know from the code above that we can specify the maximum out-of-heap memory by -xx :MaxDirectMemorySize, so we introduce two problems first

  • The default size of out-of-heap memory
  • Why call System.gc() actively

The default size of out-of-heap memory

If we didn’t pass – XX: MaxDirectMemorySize to specify the maximum heap memory, then how much is the default maximum heap memory outside, we still we see in the above code through the analysis of the code to call the sun, misc. VM. MaxDirectMemory ()

Did you mistakenly see the code above and think the default maximum is 64M? No, it’s worth starting with the initialization of the java.lang.System class

The above methods in the JVM startup of the System of this class to do initialization when the execution, so the execution time very early, we see calls inside the sun. The misc. VM. SaveAndRemoveProperties (props) :

If we pass – Dsun. Nio. MaxDirectMemorySize specifies the attribute, as long as it is not equal to 1, the effect and the added – XX: MaxDirectMemorySize same, if two parameters are not specified, DirectMemory = Runtime.getruntime ().maxMemory()

In the CMS GC case, the maximum size of the new generation – a survivor size + the maximum size of the old generation, that is, we set the value of -xmx minus one survivor size is the default out-of-heap memory size

Why call System.gc actively

If you call system.gc, you want to trigger a GC operation to reclaim out-of-heap memory, but I want to say that out-of-heap memory does not affect gc (except system.gc), but out-of-heap memory collection is dependent on our GC mechanism. The first thing we need to know at the Java level is that the only thing associated with the out-of-heap memory is the associated DirectByteBuffer object, which records the base address and size of the memory. That is, the GC can indirectly manipulate the corresponding out-of-heap memory by operating on DirectByteBuffer objects. The DirectByteBuffer object is associated with a PhantomReference when it is created. The PhantomReference is used to track when the object is collected and does not affect gc decisions. But if you find an object in the process of gc besides only PhantomReference quote it, and there is no other place to refer to it, it will be placed the references in Java. Lang. Ref. Reference. In the pending queue, The ReferenceHandler daemon is notified to perform some post-processing when gc is complete, and the PhantomReference associated with DirectByteBuffer is a subclass of PhantomReference. The Unsafe free interface is also used to address DirectByteBuffer’s out-of-heap memory block, the ReferenceHandler, in the JDK:

If pending is empty, it will be waiting for lock.wait(), where the wake up action is done in the JVM, and the following method will be called when GC is complete ::doit_epilogue(), When the pending queue is set to the pending queue, the pending queue is set to the pending queue. The pending queue is set to the pending queue

DirectByteBuffer objects and their associated out-of-heap memory will be recycled more thoroughly. This will allow DirectByteBuffer objects and their associated out-of-heap memory to be recycled. When we dump memory, we find that the DirectByteBuffer object itself is actually very small, but it can be associated with a very large out-of-heap memory, so we often refer to it as “iceberg object”. When we do YGC, we will recycle the unreachable DirectByteBuffer objects in the new generation and their out-of-heap memory, but we cannot recycle the old DirectByteBuffer objects and their out-of-heap memory, which is the biggest problem we usually encounter. If a large number of DirectByteBuffer objects were moved to old, but instead of doing CMS or full GC, we could be running out of physical memory, but we don’t know what happened. Because the heap clearly has a lot of memory left (if we disable system.gc).

Why use off-heap memory

Even though Unsafe, DirectByteBuffer uses malloc to allocate memory directly from the Unsafe native method, which does not affect gc (except system.GC). Unsafe is also the native method for processing memory. DirectByteBuffer is just a shell. Moreover, if data is stored in the heap during communication, You still end up getting a copy out of the heap and then sending it, so why not just use out-of-heap memory. For memory that requires frequent operations and is only temporary for a while, it is recommended to use out-of-heap memory and make a buffer pool to continuously recycle this memory.

Why not use a lot of off-heap memory

If we don’t have any restrictions, large area is used outside the heap memory and that can lead to memory overflow sooner or later, after all, the program is run on a resource-constrained machine, because you can directly control the memory of the recovery is not, of course, you can through other channels, such as reflection, direct use of Unsafe interface, etc., but these be sure to bring you some trouble, You completely throw away the inherent advantages of Java – development doesn’t have to worry about memory collection, and gc algorithms do it automatically. If the CMS gc or full GC is not triggered, the consequences can be serious.

Recommended reading

JVM Rookie Progression Path 11 (Eden Survivor allocation problem)

Javassist implements JDK dynamic proxies