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 tryReserveMemory
method 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 finalize
method. 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 DirectByteBuffer
configuration 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 tryHandlePending
determining 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"
openJdk Source & Notes