堆内和堆外的概念:堆内内存,heap,off-heap

硬件层面的内存,其实就是一根内存条而已

JAVA使用堆外内存导致swap飙高 java如何使用堆外内存_java

如何用堆外内存?

ByteBuffer buffer = ByteBuffer.allocateDirect(1024); // 传入的是你要申请的堆外内存的大小

你可以直接把你的数据写入到内外内存DirectByteBuffer里去

把这块数据通过Socket发送,就是直接发送就可以了,不需要走一个拷贝

堆外内存的优势?(面试:零拷贝技术)
堆内的数据,要网络IO写出去,要先拷贝到堆外内存,再写入到socket里发送出去;如果直接数据分配在堆外内存,是不需要有一次额外的拷贝的,性能是比较高的。

读写文件也是同理的,都可以节约数据拷贝次数。

-XX:MaxDirectMemorySize:通过JVM参数是可以设置你最大可以使用的堆外内存的大小的,比如说设置堆外内存最大可以使用1GB,此时已经使用了950MB空间了,然后呢,你此时要申请一块80MB的堆外内存。

会发现说,堆外内存已经不够了,此时不能直接分配堆外内存了。

DirectByteBuffer,这个对象是JVM堆内存里的一个对象,但是这个DirectByteBuffer里面包含指针,引用了一块堆外的内存。

1、如果堆外内存足够,就直接预留一部分内存

2、如果堆外内存不足,则将已经被 JVM 垃圾回收的 DirectBuffer 对象的堆外内存释放

3、如果进行一次堆外内存资源回收后,还不够进行本次堆外内存分配的话,则进行 System.gc()

4、如果 9 次尝试后依旧没有足够的可用堆外内存,则抛异常

5、实际分配内存

jvm一般分为young gc和full gc,无论是发生哪种gc,都可能会回收掉一些没有GC roots变量引用的DirectByteBuffer对象,回收掉了之后,就会主动释放他们引用的那些堆外内存,是这样子的。

DirectByteBuffer回收,就会回收关联的堆外内存,或者是内部有一个cleaner对象,可以用反射获取他,然后调用他的clean方法来主动释放内存。

如果依靠jvm gc机制,可能DirectByteBuffer躲过N次minor gc进入了老年代,然后老年代迟迟没有放满,因此迟迟没有回收,此时可能会导致DirectByteBuffer对象一直在引用堆外内存。

这样当你要分配更多的堆外内存时,无法腾出来更多的内存,就会有堆外内存溢出了。

堆内内存的OOM一样,out of memory,内存耗尽,实在是没有空闲的内存空间给你来使用了,因为所有的内存此时都别别人在使用,你要申请一块新的内存空间,实在是没有了,所以就OOM。

堆外内存的溢出,也是一样的。