同学你好,请坐,不要紧张。请问:
如何判断一个对象需不需要被回收?
采用可达性分析。即判断从一系列被叫做GC Roots的对象能否通过引用链引用到某个对象,若无法从任何GC Root引用到,则该对象就需要被回收。
为什么不使用引用计数法?
因为引用计数在循环引用的情况下会无法回收。
既然你说到了引用,谈谈你知道的Java引用类型?
强软弱幻。强引用就是我们正常说的引用,软引用则是在OOM要发生之前会被GC一次,然后假如还是空间不足,则抛出OOM。弱引用只能生存到下一次垃圾回收之前。幻引用表示这个引用其实是虚幻的,该怎么回收该对象还是怎么回收,只不过在回收之前会通知一下。
怎么判断GC Roots?
主要答:
虚拟机栈中的局部变量所引用的对象
方法区中常量引用的对象
方法区中静态变量引用的对象
可扩展答:
活动的线程所使用到的对象
JNI所引用的对象
谈谈你所知道的垃圾回收算法?
有三个基本算法和一个组合算法(分代收集算法)。他们各有自己的优点和不足。
标记-清除算法
标记从GC Roots可达的对象,清除掉其他的对象。优点在于算法简单易行,缺点在于容易产生碎片。
复制算法
将空间分成两块,每次只使用其中一块存放对象,当该块空间填满时则将GC Roots可达的对象迁移到另外一块中,并清除之前存放的块。优点在于不会产生碎片,空间干净整洁,缺点在于会浪费一部分的存储空间。
标记-整理算法
在标记-清除算法的基础之上,外加自动整理,将存活对象整理到空间前端。优点在于不会产生碎片,且空间利用率高。缺点在于比较麻烦。
分代收集算法
分代收集算法其实是一套组合拳,因为上述的垃圾回收算法各有优缺点,因此我们将对象根据他们生存期的不同划分到两个区域中,分别是新生代和老年代。新生代和老年代的空间比例默认为1:2。新生代又按照8:1:1的比例分为Eden区和两个survivor区。
Minor GC
当新生代无法放下对象时,即进行一次MinorGC
Full GC
当老年代没有足够的空间时。
何时晋升到老年代?
年龄到达15,或者对象太大年轻代放不下。
GC有什么调优参数吗?
谈谈你所知道的垃圾回收器?
新生代
新生代的所有收集器都使用的是复制算法。
Serial client状态下的首选,单线程的收集器
ParNew 多线程的Serial,更关注停顿时间,适用于交互多的场景。(记忆:New,更新的,更人性化的,因次此不是冷冰冰的关心系统吞吐量,而是关心停顿时间。)
Parallel scavenge JVM在Server状态下的默认收集器,与ParNew的区别在于?更关注系统吞吐量(用户时间/(用户时间+垃圾收集时间)),是server状态下的首选收集器,更关心cpu的使用效率
老年代
Serial-Old:老年代的单线程收集器,使用标记整理算法,Client的默认算法
Parallel Old:多线程,吞吐量优先,多线程标记整理算法,适用于cpu敏感的场景。
CMS:Concurrent Mark and Sweep:几乎能与用户线程同时工作,使用停顿敏感的场景。
分为这么几个过程:
初始标记:stw,初始地从GC Root往下进行标记,只标记到GC Root的直接对象
并发标记:并发地继续向下标记
并发预清理:查找执行并发标记阶段从年轻代晋升到老年代的对象
重新标记:stw,暂停虚拟机,重新标记
并发清理:清理对象
并发重置CMS:清理垃圾对象,程序不会停顿
混合收集器
G1:
G1(Garbage First)
复制+标记-整理算法
将整个Java堆划分为多个大小相等的Region,新生代和老年代不再物理隔离了。新生代采用复制算法,老年代采用标记整理算法。
特点是并发和并行,分代收集,并且可以整合空间,且它的停顿是可以预测的。