深入JVM虚拟机之什么是堆外内存?什么情况下发生堆外内存溢出或泄露?
堆内内存:
1.一般情况下,一个新的对象创建在JVM内的堆上,并为其分配内存空间。堆空间由JVM垃圾回收器管理,称为堆内内存(on-heap memory)
2.虚拟机会定期对垃圾内存进行回收,有时会进行一次彻底的回收Full GC
3.彻底回收时,垃圾收集器会对所有分配的堆内内存进行完整的扫描,这意味一次垃圾收集对Java 应用造成的影响,跟堆的大小是成正比的,过大的堆会影响 Java 应用的性能
堆外内存:
1.和堆内内存相对应,堆外内存就是把内存对象分配在Java虚拟机的堆以外的内存,这些内存直接受操作系统管理,而不是虚拟机
2.这样能够维护一个较小的堆,在一定程度上减少垃圾回收对应用程序造成的影响,堆外内存也被称为直接内存
3.可以使用NIO包下的DirectByteBuffer进行堆外内存的管理和使用,它会在对象创建的时候就分配堆外内存
堆外内存的优点:
1、保持了一个较小的堆内内存,可以减少垃圾收集对应哟的影响。
2、在使用缓存时,需要消耗一些内存空间来提升速度,使用堆内内存会给虚拟机带来GC压力,使用硬盘或者分布式缓存的响应时间会比较长,这时候堆外缓存会是一个比较好的选择。
3、堆内内存由JVM 管理,属于用户态,而堆外内存由操作系统管理,属于内核态。如果从堆内向磁盘些数据时,数据会被先复制到堆外内存,即内核缓冲区,然后再由操作系统写入磁盘。使用堆外内存,直接写入磁盘,提升了效率。
堆外内存的缺点:
1、堆外内存的缺点就是内存难以控制。
2、使用堆外内存就间接失去了使用JVM管理内存的可能性,一旦发生内存溢出时排查起来非常困难。
堆外内存泄露:
1.操作系统对每个进程能管理的内存都是有限制的,对于32位系统而言,Linux最大接近4G,Windows大概2G,如果堆内实际内存1.6G,那留给堆外内存的大小也就0.4G
2.堆外内存不会像新生代和老年代那样,发现空间不足就通知收集器进行回收
3.它只能等待老年代满了后触发Full GC,然后JVM会“顺便地”清理堆外内存中废弃的对象
4.在极端情况下,如果JVM一直没有执行Full GC的话, 堆外内存也一直得不到释放
堆外内存泄露的干预方法:
1.需要一种机制能够主动对它进行清理,需要显示的调用System.gc()
2.但假如打开了 -XX:+DisableExplicitGC 开关,禁止显示调用System.gc()
3.假如堆外内存空间已经满了,虽然肯堆中还有很多空闲的内存,堆外内存也不得不抛出内存溢出异常
堆外内存回收过程:
1.JVM在堆内只保存堆外内存的引用,用DirectByteBuffer对象来表示
2.每个DirectByteBuffer对象在初始化时,都会创建一个对应的Cleaner对象
3.当DirectByteBuffer对象在某次GC中被回收,只有Cleaner对象知道堆外内存的地址
4.当下一次GC执行时,Cleaner对象会触发自身clean方法清理对应的堆外内存
5.Cleaner对象在下次GC时被回收
总结:
1.堆外内存有良好的伸缩性,对垃圾回收停顿的改善可以明显感觉到,提高了IO效率
2.堆外内存的使用需要我们重点关注,当发生内存溢出时排查起来非常困难