JVM灵魂拷问系列之垃圾回收
1. 垃圾回收发生在哪些区域?
JVM内存模型中, 线程共享区域 会发生垃圾回收,也就是 堆内存 和 方法区
线程独占区域的内存,会随着线程的消亡而销毁,不需要垃圾回收
2. 说说JVM在哪些情况下会进行垃圾回收?
Eden区域/永久代内存满了会进行垃圾回收
一般有通过两种方式来判断:引用计数法 和 可达性分析
2.1 引用计数法
通过对象的引用计数器来判断该对象是否被引用,被其他地方引用一次+1,反之-1,值为0表示没有引用可回收
缺点:循环引用的情况无法解决,所以Java没用使用引用计数法
2.2 可达性分析
以根对象为起点向下搜索,走过的路径为引用链,如果对象到根对象没用引用链相连,则认定这个对象不可达,是可以回收,如图Object5和Object7是可回收的
哪些对象可以作为根对象
- 虚拟机栈(栈帧中的本地变量表)引用的对象
- 方法区类静态属性引用的对象
- 方法区常量引用的对象
- 本地方法栈中JNI(即Native方法)引用的对象
3. 说说强引用、软引用、弱引用和虚引用,在垃圾回收时会发生什么?
- 强引用:平时使用的引用,Object o = new Object();,只要强引用在对象就不会被回收.哪怕出现内存溢出也不回收
- 软引用:SoftReference
sf = new SoftReference<>(“hello”);一般是描述一些有用但非必需的对象。内存不足才回收
软引用比较适合用来实现缓存,比如网页缓存图片缓存等等 - 弱引用:WeakReference
sf = new WeakReference<>(“hello”);一般是描述一些有用但非必需的对象。不管内存是否不足都会被回收 - 虚引用:任何时候都可能被回收,主要用来跟踪被回收的活动,一般跟引用引用队列配合使用
4.说说JVM的垃圾回收算法?对象什么时候转移到老年代?
首先,我们需要了解一个原则:JVM一边运行代码一边判断和清理垃圾对象是不现实的,所以提到垃圾回收都会说一个概念: Stop The World,意思是JVM会停止工作线程的运行来进行垃圾回收 -> 发生小小的卡顿
例子:聚会(程序运行)不断产生垃圾,清洁工清理垃圾(垃圾收集器),如果聚会不停止(程序不短暂暂停),则清洁工就没办法进入场地清理垃圾,打扫好屋子
JVM使用 分代收集算法 来进行垃圾回收,根据不同区域使用不同垃圾回收算法,既可以有效清除垃圾对象,又提高了垃圾回收的效率
这里省略另一种算法 增量算法:每次只收集一小片区域的垃圾(每次回收全部垃圾系统停顿更长,此算法可减小系统停顿)
4.1 年轻代垃圾回收算法-复制算法
Minor/Young GC
98%的对象都会很快消亡,所以 大多数对象的生命周期都很短
流程:对象在创建时,一般会先存放到Eden,Eden满了之后就会触发垃圾回收,会把Eden中存活对象拷贝到S1中,清理Eden区域的所有对象。如果Eden区域又满时,会把Eden区域和S1的存活对象复制到S2区域,再清楚所有Eden区域和S1区域的对象。如此反复..
优点:性能好、无碎片
缺点:内存利用率低
4.2 老年代垃圾回收算法-标记清除/标记整理算法
老年代的对象都是被长期引用的,老年代的垃圾回收比较慢,一般是年轻代垃圾回收的10倍以上
新创建的对象也有可能直接进入老年代:
- 对象大于JVM参数-XX:PretenureSizeThreshold的阈值时,直接分配到老年代
- 新生代的空间不足以存放该对象
老年代的垃圾回收:Major GC
执行Major GC时一般都伴随这Minor GC,所以老年代回收约等于Full GC。
Major GC 触发条件:
- 老年代空间不足
- 元空间不足
- 某一对象晋升到老年代但是老年代空间不足
- 显式调用
System.gc()
方法,可以使用-XX:+DisableExplicitGC来忽略此调用
标记清除算法
- 使用 可达性分析 标记可回收对象
- 清除可回收对象
优点:实现简单
缺点:清除后会存在内存碎片,例如图中情况下,如果想分配一个连续三个内存空间的数组,则图中的内存块无法做到
标记整理算法
- 使用 可达性分析 标记可回收对象
- 把存活对象移动到一端
- 清除可回收对象
优点:无内存碎片
缺点:对比标记清除开销大
4.3 对象什么时候转移到老年代
- 经过 15次Young GC还存活 的对象会转移到老年代。例如Spring容器里管理的对象实例,被Spring容器引用,所以长期存活转移到老年代
- 新创建的大对象,如果Eden区域放不下,则会转移到老年代
- S区域放不下 的对象会转移到老年代
5. GC简单汇总图
Full GC待补充
6. 常用的垃圾回收器
parnew + CMS
目前主流的jdk版本使用的组合
parnew: 新生代垃圾回收器,使用多线程去回收垃圾对象,使用复制算法
CMS:老年代垃圾回收器,是一个并发收集器,使用标记清除算法
G1分代垃圾回收器
jdk新版本使用的垃圾回收器