In our daily development process, in addition to common exceptions (null pointer, array out of bounds and so on), we have encountered relatively big problems are OOM, frequent FullGC or multithreading problems (I cannot say 🌚), most of our problems are related to JVM. And today I’m going to talk about another place related to it.

NonHeap

As A Java developer, the first thing we’re familiar with (though we’re not clear about the various recycling algorithms inside) is the JVM, which manages the life cycle of objects (yes, we all have objects 🤔) to keep our programs running. But there is another area just across the bank from it -> non-heap memory (figure below).

We can clearly see where NonHeap is in the program (the diagram above does not represent the percentage of memory they take up).

role

What we can be sure of is that what’s in the heap is for us to manipulate, and NonHeap is for the JVM to use for itself, So the method area, the memory needed for processing or optimization within the JVM (such as jIT-compiled code caches), the code for each class structure (such as runtime pool, field, and method data), and the method and constructor code are all in non-heap memory.

This is a small Demo where we can use Arthas to look at heap space and non-heap space as well as partition areas.

use

Normal developers should not use it (like me 🌚🌚🌚), advanced developers should use it, because they know how to make an ordinary application extraordinary.

In JAVA, non-heap memory can be handled through the Unsafe and ByteBuffer packages under the NIO package.

Unsafe

A look at this name know not safe 😹, but also really not how safe. It is a class under the Sun.misc package that mainly provides methods for performing low-level, insecure operations. Most of the internal API is the direct operation of the system memory, which will improve the efficiency of our program and so on, but it will also be prone to errors, he inside operation similar to the C language pointer operation, will increase the risk of pointer problems related to the program.

We can do a little 👻 Kangkang 👻 some of them:

// allocate memory, equivalent to the C++ malloc function
public native long allocateMemory(long bytes);
// Expand the memory
public native long reallocateMemory(long address, long bytes);
// Free memory
public native void freeMemory(long address);
// Sets the value in the given memory block
public native void setMemory(Object o, long offset, long bytes, byte value);
// Memory copy
public native void copyMemory(Object srcBase, long srcOffset, Object destBase, long destOffset, long bytes);
// Gets the given address value, ignoring the access restriction of the modifier qualifier. Similar operations are getInt, getDouble, getLong, getChar, and so on
public native Object getObject(Object o, long offset);
PutInt,putDouble, putLong, putChar, putInt,putDouble, putLong, putChar, etc
public native void putObject(Object o, long offset, Object x);
// Get a byte value for the given address (this method is certain if and only if the memory address is allocated to allocateMemory)
public native byte getByte(long address);
// Set a byte value for the given address (this is certain if and only if the memory address is allocated to allocateMemory)
public native void putByte(long address, byte x);
Copy the code

In addition to the above memory-related methods, CAS methods, such as concurrent collection operations under J.U.C and related locking operations, are mostly governed by calls to Unsafe methods.

DirectByteBuffer

DirectByteBuffer is an important Java class for implementing out-of-heap memory. It is usually used as a buffer pool during communication, and is widely used in NIO frameworks such as Netty. DirectByteBuffer also uses the out-of-heap API to create, use, and destroy out-of-heap memory. Its constructor allocates memory directly.

Related API Kangkang:

// Allocate memory size
 unsafe.allocateMemory(size);
 // Initialize the size of memory from base
 unsafe.setMemory(base, size, (byte) 0);
Copy the code

recycling

Heap collection depends on the implementation of the collection algorithm for each region of the JVM. How about non-heap collection? And under what circumstances?

So far, there are two ways:

  1. Its garbage collection relies on the code explicitly calling System.gc().
  2. Rely on garbage collection trace object Cleaner to realize the memory release.

The first will not be discussed much. The second kind is mentioned here:

We use DirectByteBuffer source code to check the current class structure, the main attention is that the current object contains a Deallocator private static internal class and private member attribute Cleaner:

    private final Cleaner cleaner;

    private static class Deallocator implements Runnable
    {
        private static Unsafe unsafe = Unsafe.getUnsafe();
        private long address;
        private long size;
        private int capacity;
        
        private Deallocator(long address, long size, int capacity) {
            assert(address ! =0);
            this.address = address;
            this.size = size;
            this.capacity = capacity;
        }
        
        public void run(a) {
            if (address == 0) {
                // Paranoia
                return;
            }
            unsafe.freeMemory(address); // Free memory
            address = 0; Bits.unreserveMemory(size, capacity); }}Copy the code

From the above we can see that the last non-heap memory reclamation must be done by static inner classes. It’s also related to a member variable, so how does it operate?

What we should pay attention to here is Cleaner object.

Cleaner inherits from a virtual reference PhantomReference, one of Java’s four main reference types (we’ve learned that you can’t get an object instance associated with a virtual reference, and when an object is referenced only by a virtual reference, it can be reclaimed whenever a GC occurs), PhantomReference is usually combined with the ReferenceQueue of the ReferenceQueue to realize system notification and resource cleaning when the virtual reference associated object is garbage collected. As shown in the following figure, when an object referenced by the Cleaner is about to be collected, the JVM garbage collector places the Reference to the object in the pending list of the object Reference and waits for reference-handler to process it. Reference-handler is a daemon thread with the highest priority, which processes object references in pending lists continuously and executes Cleaner clean method for related cleaning.

Therefore, when DirectByteBuffer is only referenced by the Cleaner (i.e. a virtual reference), it can be collected in any GC period. When the DirectByteBuffer instance object is collected, in the reference-handler thread operation, the Cleaner’s clean method is called to free the out-of-heap memory according to the Deallocator passed in when the Cleaner is created.

role

We’ve seen the role of heap, so we’re wondering what role non-heap plays in our program?

Two points are summarized:

  1. Improvements to garbage collection pauses. Because out-of-heap memory is managed directly by the operating system and not the JVM, we can keep the in-heap size small when using out-of-heap memory. This reduces the impact of collection pauses on applications during GC.
  2. Improved application I/O performance. During I/O communication, data is copied from memory in the heap to memory out of the heap. Temporary data that requires frequent data copying and has a short life cycle is recommended to be stored in memory out of the heap.

The first point:

We know that all GC in THE JVM is for the collection of the objects in the current container. In the Ygc stage, the garbage marking process is involved, starting from GCRoot. Once the objects referenced to the old era are scanned, the scanning is interrupted to accelerate the PROGRESS of Ygc. However, the old-Gen Sacnning stage in THE Ygc phase is used to scan objects referenced by the old age, so once the old age is too large, the Ygc takes too long (time is proportional to the size), which is not conducive to the garbage collection of the current program. So once non-heap is introduced, we can keep the heap memory size small to keep GC going.

Second point:

It involves the main user mode and kernel mode on the server, we know that a file transfer operation on the server, will involve the user mode to kernel mode, and then turn the kernel mode user mode, and so on steps, some of the operation is consumption of CPU resources (buffer read and write from memory address), we will be one of the operation can be omitted, We can move files directly from disk to the memory address cache and then to the socket buffer, which is called zero-copy technology.

At the end

That’s a brief overview of what non-heap does in Java. Use non-heap I think most programmers should not use (I am not touch), but we can understand, growth of knowledge is correct 🙈🙈. Finally, I wish you all a good year ~