Detailed outside Java heap memory

Chinese New Year approaching, most recently a little time to prepare along the idea to write down the last column, I recommend you read: juejin.im/post/5e19d6...

Wuhan eat wild game that a few silly [], ask your mother hid

Start of Text

When you run a Java program, java virtual machine needs to use memory to store a variety of data. java virtual machine specification of these memory areas called the run-time data area:

The external heap memory, refers to the distribution outside the java heap memory area, which is not jvm management, it will not affect the gc.

This will be java.nio.DirectByteBuffer for example, to analyze the external heap memory.

    // Primary constructor
    //
    DirectByteBuffer(int cap) {                   // package-private

        super(-1, 0, cap, cap);
        boolean pa = VM.isDirectMemoryPageAligned();
        int ps = Bits.pageSize();
        long size = Math.max(1L, (long)cap + (pa ? ps : 0));
        Bits.reserveMemory(size, cap);

        long base = 0;
        try {
            base = unsafe.allocateMemory(size);
        } catch (OutOfMemoryError x) {
            Bits.unreserveMemory(size, cap);
            throw x;
        }
        unsafe.setMemory(base, size, (byte) 0);
        if (pa && (base % ps != 0)) {
            // Round up to page boundary
            address = base + ps - (base & (ps - 1));
        } else {
            address = base;
        }
        cleaner = Cleaner.create(this, new Deallocator(base, size, cap));
        att = null;
    }
复制代码

Memory book

As can be seen from the construction method DirectByteBuffer, the heap memory allocation starts at outside Bits.reserveMemory(size, cap);in.

Bits into the class, look at the property and several members of the heap outside the memory-related:

    private static volatile long maxMemory = VM.maxDirectMemory();
    private static final AtomicLong reservedMemory = new AtomicLong();
    private static final AtomicLong totalCapacity = new AtomicLong();
    private static final AtomicLong count = new AtomicLong();
    private static volatile boolean memoryLimitSet = false;
复制代码

maxMemory

The maximum amount of memory allocated off heap set by the user, the parameters jvm -XX:MaxDirectMemorySize=configuration.

reservedMemory

It has been used outside the heap memory size. Use AtomicLong to ensure the safety of multi-threaded.

totalCapacity

total capacity. Also use AtomicLong.

count

Record total parts outside the heap allocation of memory.

memoryLimitSet

A flag variable, there volatile keyword. Used to record whether maxMemory field has been initialized.


Before assigning external heap memory, jdk using the tryReserveMemorymethod achieves an optimistic lock to ensure the upper limit on the total number of heap memory allocation is not greater than the actual design.

private static boolean tryReserveMemory(long size, int cap) {

        // -XX:MaxDirectMemorySize limits the total capacity rather than the
        // actual memory usage, which will differ when buffers are page
        // aligned.
        long totalCap;
        while (cap <= maxMemory - (totalCap = totalCapacity.get())) {
            if (totalCapacity.compareAndSet(totalCap, totalCap + cap)) {
                reservedMemory.addAndGet(size);
                count.incrementAndGet();
                return true;
            }
        }

        return false;
    }
复制代码

TryReserveMemory logic is also relatively simple, while loop + CAS used to ensure there is sufficient free space, and updates the total space, the remaining space, and the number of external memory heap.

As can be seen, if the CAS fails, but there is enough capacity, while the cycle will enter the next round of CAS update attempts, until the update is successful or insufficient capacity.

In the following code fragment, written comments is clear: the references into force under the pending state and try again, if the reference contained in the corresponding Cleaner, it will help release the heap memory outside.

        final JavaLangRefAccess jlra = SharedSecrets.getJavaLangRefAccess();

        // retry while helping enqueue pending Reference objects
        // which includes executing pending Cleaner(s) which includes
        // Cleaner(s) that free direct buffer memory
        while (jlra.tryHandlePendingReference()) {
            if (tryReserveMemory(size, cap)) {
                return;
            }
        }
复制代码

In tryHandlePendingReference method, only four lines of code:

        SharedSecrets.setJavaLangRefAccess(new JavaLangRefAccess() {
            public boolean tryHandlePendingReference() {
                return Reference.tryHandlePending(false);
            }
        });
复制代码

No one who saw the phantom reference a column to explain to readers here already know what to do here is outside the heap released:

jlra.tryHandlePendingReference()Jdk actually call the method and process pending in the state of reference is the same method Reference-handler thread calls.

About Reference-handler thread, see: juejin.im/post/5e19d6...

Subsequently, jdk will take the initiative to call aSystem.gc();

In reserveMemory process, only the outer memory heap first set value related attributes, but no real memory allocation.

Allocate memory

After a predetermined external memory heap success, jdk calls the method unsafe to do in heap memory allocation outside.

    base = unsafe.allocateMemory(size);
复制代码

allocateMemory is a native method, for allocating memory heap outside. In unsafe.cpp, you can see his source:

UNSAFE_ENTRY(jlong, Unsafe_AllocateMemory0(JNIEnv *env, jobject unsafe, jlong size)) {
  size_t sz = (size_t)size;

  sz = align_up(sz, HeapWordSize);
  void* x = os::malloc(sz, mtOther);

  return addr_to_java(x);
} UNSAFE_END
复制代码

Call malloc function to allocate memory and return address.

Release heap memory outside

We know, jvm in java object is to use gcRoot do reachability analysis to determine whether the recovery, while external memory heap is not associated with gcRoot, how to know when it should reclaim heap memory outside of it?

Is the ideal solution: DirectByteBuffer when the corresponding object instance is recovered, the recovery synchronized external memory heap.

At this time the students should have thought of finalizemethod. This may be a method to a java c group compromise, when the object to be recovered, called by gc. Looks a little like a destructor c is, But , calling the method is unreliable and can not guarantee that the object will be recovered before gc call this method.

In jdk, the embodiment is the use of references to release the virtual external memory heap. In the DirectByteBufferconfiguration of the method, there is a line of code as follows:

    cleaner = Cleaner.create(this, new Deallocator(base, size, cap));
复制代码

DirectByteBuffer cleaner attribute is a virtual reference.

In Deallocator, the same native method using the unsafe outside the heap to free memory.

    unsafe.freeMemory(address);
    address = 0;
    Bits.unreserveMemory(size, capacity);```
复制代码
UNSAFE_ENTRY(void, Unsafe_FreeMemory0(JNIEnv *env, jobject unsafe, jlong addr)) {
  void* p = addr_from_java(addr);

  os::free(p);
} UNSAFE_END
复制代码

cleaner call point is located Reference-handler thread Reference class.

When reachability change the referenced object, a reference state to a pending state, it will be in tryHandlePendingdetermining whether the currently referenced process Cleaner instance, if so, the method calls its clean, complete external memory heap recovered.

other

When a predetermined memory, why should take the initiative to call System.gc

The following quote from the Cold Spring Son blog ( lovestblog.cn/blog/2015/0... ):

Since you want to call System.gc, it is certainly want to trigger a heap outside the gc operation to recover the memory, but I would like to say is outside the heap memory will not cause any impact on the gc (except System.gc here), but the heap external memory recovery actually rely on our gc mechanism, we need to know DirectByteBuffer objects in java only associated with the level and relevance of this memory we allocated out of the heap, and it records this memory base address and size, and since then the gc also related that gc can operate DirectByteBuffer indirect object corresponding to the operation of the external heap memory. DirectByteBuffer objects in the creation of an association PhantomReference, it comes PhantomReference it is mainly used to track when the object is recovered, it can not affect the decision-making gc, gc process but if an object is found only in addition to reference it outside PhantomReference and there is no other place to refer to it, and that will put this quote into java.lang.ref.Reference.pending queue, notify ReferenceHandler this daemon thread when finished gc to perform some post-processing, and associated DirectByteBuffer is a subclass of PhantomReference PhantomReference in the final process will be released in an outer DirectByteBuffer corresponding block of heap memory is free through the interface Unsafe

Reference material

"Write himself java virtual machine"

lovestblog.cn/blog/2015/0…

openJdk Source & Notes

Guess you like

Origin juejin.im/post/5e254c1ff265da3e0c4c7ac6