java 面试知识点笔记(四)垃圾回收 上篇

落花浮王杯 提交于 2019-12-01 02:09:29

问:对象判定为垃圾的标准?

没有被其他对象引用

问:对象判定为垃圾的算法?

引用计数算法

  • 通过判断对象的引用数量来判断对象是否可以被回收
  • 每个对象实例都有一个引用计数器,被引用则+1,完成引用-1
  • 任何引用计数为0的对象实例可以当垃圾收集的

    优点:执行效率高,程序受影响较小

    缺点:无法检测出循环引用的情况,导致内存泄漏memory leak

可达性分析算法

  • 通过判断对象的引用链是否可达来决定对象是否可以被回收

可以作为GC Root的对象

  • 虚拟机栈中的引用对象(栈帧中的本地变量表)
  • 方法区中的常量引用对象
  • 方法区中的类静态属性引用的对象
  • 本地方法栈中JNI(Native方法)的引用对象
  • 活跃线程的引用对象

问:谈谈你了解的垃圾回收算法?

标记-清除算法(mark and sweep)

  • 标记:从根集合进行扫描,对存活的对象进行标记
  • 清楚:对堆内存从头到尾进行线性遍历,回收不可达对象内存,并把原来标记为可达的标记清除掉

优点:效率高,因为不需要对对象的移动操作。

缺点:碎片化,标记清除之后会留下大量不连续的内存空间,碎片太多可能会导致以后程序运行过程中无法提供连续内存,而不得不进行另一次垃圾回收

 

复制算法(Copying)

  • 分为对象面和空闲面
  • 对象在对象面上创建
  • 存活的对象从对象面复制到空闲面
  • 将对象面所有对象内存清除

优点:

  • 解决了碎片化的问题 (每次复制到空闲面的对象都是连续排放的)
  • 顺序分配内存,简单高效 (每次直接清理一半的内存空间,所以简单高效)
  • 适用于对象存活率低的场景,比如年轻代(现在成熟的商业虚拟机中都采用这种算法回收年轻代,因为年轻代一般只有10%的对象存活,所以使用这种算法效率还不错)

缺点:在应对对象存活率较高时就有些力不从心了,因为有较多的复制操作,效率将会变低。而且不想浪费额外的50%空间,就需要更多的空间进行担保,因为需要应对所有对象都100%存活的极端情况,所以老年代一般不能选用这种算法。

 

标记-整理算法(Compacting)

  • 标记:从根集合进行扫描,对存活的对象进行标记
  • 清除:移动所有存活的对象,且按照内存地址次序一次排序,然后将末端内存地址以后的内存全部回收

优点:解决了内存碎片化的问题,清理之后内存是连续的,也不用设置两块内存互换,适用于存活率较高的场景

缺点:需要移动标记存活的对象,成本较高

分代收集算法(Generational Collector) 现在主流的GC算法

  • 垃圾回收算法的组合拳
  • 按照对象生命周期的不同划分区域以采用不同的垃圾回收算法
  • 目的:提高JVM的回收效率

JDK8之后没有永久代,但是年轻代、老年代都保留了下来,年轻代使用的是复制算法,老年代使用的标记整理算法

问:分代收集算法的GC分几种?

  • Minor GC (发生在年轻代中的垃圾收集工作,使用复制算法。年轻代是所有java对象出生的地方,新对象一般存活率较低,所以MinorGC比较频繁)
  • Full GC 

年轻代:

  • Eden区(就是伊甸园,代表这人类的起源,新对象存放的区域,如果eden区放不下新对象,也有可能会放在survivor区甚至是老年代中)
  • 两个Survivor区(幸存者区,from区和to区,这两个区也不是固定的,会随着垃圾回收而相互转换)

默认是8:1:1的比例分配,垃圾清理时会将eden和from区的存活对象一次性复制到to区,to区不够用的时候需要老年代做担保

年轻代垃圾回收的过程演示:

每次存活就会标记+1,默认是15岁,也可以设置-XX:MaxTenuringThreshold设置最大年龄限制,到达限制就会进入老年代,当然如果eden或者survivor装不下也会进入老年代

对象如何晋升为老年代:

  • 经历一定Minor次数依然存活的对象
  • Survivor区放不下的对象
  • 新生成的大对象(可以设置-XX:+PretenuerSizeThreshold设置大对象大小,超过这个大小的大对象会直接放入老年代)

常用的调优参数:

  • -XX:SurvivorRatio:Eden和Survivor的比值,默认8:1
  • -XX:NewRatio:老年代和年轻代内存大小比例,比如2表示老年代是年轻代的2倍

老年代:存放生命周期较长的对象

  • 标记-清理算法
  • 标记-整理算法

一般会伴随着新生代的回收及整个堆的回收(FullGC和MajorGC,FullGC比MinorGC慢,但执行频率低)

问:出发FullGC的条件?

  1. 老年代空间不足(最好不要创建太大对象)
  2. 永久代空间不足(JDK7以前的版本,这也是JDK8使用元空间替代永久代的原因,降低了FullGC的频率)
  3. CMS GC时出现promotion failed ,concurrent mode failure(注意日志里是否出现这两个情况,这两种情况很容易触发FullGC)
  4. Minor GC 晋升到老年代的平均大小大于老年代剩余空间(hotspot在进行MinorGC做了个统计,如果晋升到老年代的平均大小大于老年代剩余空间就直接触发Full GC)
  5. 调用System.gc()(这只是程序提醒虚拟机,码农希望这里进行一下回收,但回不回收还是要看JVM)
  6. 使用RMI来进行RPC或者管理JDK应用,默认每小时执行一次FullGC

 

 

 

 

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!