徐无忌深入JVM虚拟机笔记:什么是堆外内存?什么情况会发生堆外内存溢出或泄露?

完成:第一遍

1.什么是堆内内存?

一:一般情况下,一个新的对象创建在JVM内的堆上,并为其分配内存空间。堆空间由JVM垃圾回收器管理,称为堆内内存(on-heap memory)

二:虚拟机会定期对垃圾内存进行回收,有时会进行一次彻底的回收Full GC

三:彻底回收时,垃圾收集器会对所有分配的堆内内存进行完整的扫描,这意味一次垃圾收集对Java 应用造成的影响STW,跟堆的大小是成正比的,过大的堆会影响 Java 应用的性能

2.什么叫堆外内存?

一:和堆内内存相对应,堆外内存就是把内存对象分配在Java虚拟机的堆以外的内存,这些内存直接受操作系统管理,而不是虚拟机

二:这样能够维护一个较小的堆,在一定程度上减少垃圾回收对应用程序造成的影响,堆外内存也被称为直接内存

三:可以使用NIO包下的DirectByteBuffer进行堆外内存的管理和使用,它会在对象创建的时候就分配堆外内存

3.堆外内存优点有哪两个?

优点一:保持一个较小的堆内内存,可以减少了垃圾收集对应用的影响

优点二:在使用缓存时,需要消耗一些内存空间来提升速度,使用堆内内存会给虚拟机带来GC压力,使用硬盘或者分布式缓存redis的响应时间会比较长,这时候堆外缓存(比如ohs框架)会是一个比较好的选择

优点三:堆内内存由JVM管理,属于用户态
而堆外内存由操作系统管理,属于内核态
内核态(Kernel Mode):运行操作系统程序
用户态(User Mode):运行用户程序

优点四:堆外内存进行IO操作效率更高
如果从堆内向磁盘写数据时,
阶段一:数据会被先复制到堆外内存,即内核缓冲区,
阶段二:然后再由操作系统写入磁盘
使用堆外内存,直接写入到磁盘中,提升了效率,比如零拷贝技术

4.堆外内存的缺点有哪两个?

缺点一:堆外内存的缺点就是内存难以控制

缺点二:使用了堆外内存就间接失去了JVM管理内存的可行性,改由自己来管理,当发生堆外内存溢出时排查起来非常困难

5.为什么会出现堆外内存泄漏?

操作系统对每个进程能管理的内存都是有限制的,对于32位系统而言,Linux最大接近4G,Windows大概2G,如果堆内实际内存1.6G,那留给堆外内存的大小也就0.4G

堆外内存不会像新生代和老年代那样,发现空间不足就通知收集器进行回收,比如老年代内存利用率到92%
它只能等待老年代满了后触发Full GC,然后JVM会“顺便地”清理堆外内存中废弃的对象

在极端情况下,如果JVM一直没有执行Full GC的话, 堆外内存也一直得不到释放

6.如何显式对堆外内存进行清理?

需要一种机制能够主动对它进行清理,需要显示的调用System.gc()

但假如打开了 -XX:+DisableExplicitGC 开关,禁止显示调用System.gc()
假如堆外内存空间已经满了,虽然肯堆中还有很多空闲的内存,堆外内存也不得不抛出内存溢出异常

7.堆外内存回收的整个过程是什么样子?

步骤一:JVM在堆内只保存堆外内存的引用,用DirectByteBuffer对象来表示

步骤二:每个DirectByteBuffer对象在初始化时,都会创建一个对应的Cleaner对象

步骤三:当DirectByteBuffer对象在某次GC中被回收,只有Cleaner对象知道堆外内存的地址

步骤四:当下一次GC执行时,Cleaner对象会触发自身clean方法清理对应的堆外内存

步骤五:Cleaner对象在下次GC时被回收