import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.List;
/** * @author congzhou * @description: * @date: Created in 2019/2/19 21:57 */
public class NativeHeapTest {
public static void main(String[] args) throws InterruptedException {
List<ByteBuffer> byteBufferList = new ArrayList<>();
while (true) {
ByteBuffer byteBuffer = ByteBuffer.allocateDirect(10240);
byteBufferList.add(byteBuffer);
Thread.sleep(100);
}
}
}
复制代码
watch命令观察变化内存 html
ByteBuffer.allocateDirect调用了Unsafe.allocateMemory(long var1),该方法是个native方法 github.com/unofficial-…java
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
复制代码
主要就是os::malloc(sz, mtInternal)方法,该方法封装了C++的标准库std::malloc() github.com/unofficial-…linux
void* os::malloc(size_t size, MEMFLAGS flags) {
return os::malloc(size, flags, CALLER_PC);
}
void* os::malloc(size_t size, MEMFLAGS memflags, const NativeCallStack& stack) {
/*省略代码*/
u_char* ptr;
ptr = (u_char*)::malloc(alloc_size);
/*省略代码*/
// we do not track guard memory
return MemTracker::record_malloc((address)ptr, size, memflags, stack, level);
}
复制代码
(u_char*)::malloc(alloc_size)就是C++标准库函数,传入空间大小返回内存地址。git
openJDK sun.nio.ch.IOUtil.write(),java.nio.channels.SocketChannel#write(java.nio.ByteBuffer)等方法底层实现github
static int write(FileDescriptor fd, ByteBuffer src, long position, NativeDispatcher nd) throws IOException {
if (src instanceof DirectBuffer)
return writeFromNativeBuffer(fd, src, position, nd);
// Substitute a native buffer
int pos = src.position();
int lim = src.limit();
assert (pos <= lim);
int rem = (pos <= lim ? lim - pos : 0);
ByteBuffer bb = Util.getTemporaryDirectBuffer(rem);
try {
bb.put(src);
bb.flip();
// Do not update src until we see how many bytes were written
src.position(pos);
int n = writeFromNativeBuffer(fd, bb, position, nd);
if (n > 0) {
// now update src
src.position(pos + n);
}
return n;
} finally {
Util.offerFirstTemporaryDirectBuffer(bb);
}
}
复制代码
上述代码直观的展现了java堆内存首先拷贝到了直接内存,而后再把地址传给I/O函数。
java GC三大类算法,除了标记清除,标记整理和复制算法都会移动对象,而且若是直接把java堆地址传给I/O函数则须要保证I/O操做过程当中该块内存不变化,则须要暂停GC,因此JDK实现使用拷贝的方式。算法
操做系统I/O过程当中,须要把数据从用户态拷贝到内核态,而后再输出到I/O设备,因此从java堆内存输出到I/O设备需通过两次拷贝,而direct memory在native 堆上,因此只需通过一次拷贝。centos
[1]www.kernel.org/doc/Documen…
[2]www.linuxjournal.com/article/634…
[3]www.ibm.com/developerwo…
[4]zhuanlan.zhihu.com/p/27625923
[5]www.zhihu.com/question/57…函数