GC垃圾回收机制详解

JavaGC,基本上是两年以上经验面试必问的面试题,今天咱们来扒一扒他。
Java GC就是所谓的垃圾回收.今天来通过5w1h法,你如果记住这六个点,对于垃圾回收你就算基本掌握了。

  • 什么是GC
  • GC所作用的空间
  • GC的执行时间
  • GC的都执行了什么

一.什么是GC?

GC就是我们平常所说的垃圾回收。在程序运行过程中,会有很多的对象不再使用但是还占用着内存,这些就是内存里的垃圾,如果不及时清除就会造成内存溢出,那么怎样及时的把内存的垃圾清理出来,就是GC要做的事情。

二.GC所作用的空间是哪?

jvm中,程序计数器、栈区和方法区的生命周期都是与线程一致的,是随线程生而生,随线程灭而灭的,栈区会随方法的开始和结束进行入栈和出栈,所以栈区实现了自动清理,因此垃圾回收主要是在堆区和方法区进行垃圾回收。

三.GC什么时候执行呢?

1.system.gc()
2.系统自身决定GC触发的时机,主要是在年轻代和老年代部分,他们分别有不同的执行时间。
GC分为Minor GC(年轻代GC)和Full GC(老年代GC)
Minor GC的触发条件:当Eden区满时,触发Minor GC。
Full GC 的触发条件:
a.老年代空间不足时;
b.年轻代GC完成后,老年代盛不下时;
c.方法区空间不足时;

四.GC都执行了什么

要想进行垃圾回收,咱们得先垃圾的标准定义出来,现在有两个垃圾定义的标准。

1.怎么判断一个对象可以被可以被回收呢(成为垃圾呢)?

1)引用计数:每个对象,都有一个计数属性,每增加一个引用对象,就会对计数加1,当计数属性为0时,就代表这个对象不再被使用,可以回收了。这个方法不能解决对象之间相互引用的问题。
2)可达性分析算法:GC root开始向下搜索,搜索走过的路径即为引用链,当这个对象到root没有引用链时,就代表这个对象不可用,这也是jvm当前使用就是该算法。

GC垃圾回收机制详解

2.常用的垃圾回收算法

GC常用算法有:标记-清除算法,标记-压缩算法,复制算法,分代收集算法。
目前主流的JVM(HotSpot)采用的是分代收集算法。

(1)标记-清除算法

在标记阶段,给每一个对象记录一个是否清除的标记,记录对象的存活状态(活着or死亡);在清除阶段,把标记为死亡的对象清除掉,也就是执行GC。只是该方法因为不会移动对象,所以会产生大量的垃圾碎片。会造成内存的浪费。
GC垃圾回收机制详解

(2)标记-整理算法

标记整理是标记清除的改进版,在标记阶段,给每个对象记录是否清除的标记,记录对象的存活状态(活着or死亡)。在整理阶段,把已使用所有存活的对象整理一下,然后把其他对象都清除掉。这就是标记-整理算法。该算法解决了内存碎片的问题,但是如果存活对象比较多的话,效率会降低。
GC垃圾回收机制详解

(3) 复制算法

该算法把内存分为两块,每次只使用一半,暂叫它A区和B区,假如当前使用的是A区,回收时,
会把A区的存活的对象都复制到B区,然后把A区清空,如果清空B区,再把存活对象复制到A区,B区清空。这种算法既解决了内存碎片问题,也解决了效率问题,但是该算法会浪费一半的内存空间。
GC垃圾回收机制详解

(4)分代收集法

现在虚拟机垃圾收集大多采取这种方式,jvm就是之一。它根据对象的存活时间分为 年轻代(Young)和老年代(Tenure)。因为在年轻代的对象生存周期短,所以使用了复制算法,而老年代对象存活率较高,所以使用标记-清除或者标记-整理算法。
具体来说,年轻代(Young)又分为 伊甸园区(Eden区)和From区和To区
GC垃圾回收机制详解
当系统创建新对象时,总是在Eden区,当Eden区被占满时,就会触发一次YoungGC,即年轻代的垃圾回收。它会把Eden区存活的对象复制到From区。
GC垃圾回收机制详解
这时,Eden区已经被清空了,可以继续创建新对象了,当Eden再次满时,就触发youngGC,注意,这个次不太一样哦,这次GC,它会把Eden区和From区的存活对象都复制到To区。
GC垃圾回收机制详解
再下次YoungGC时,就会把To区和Eden区的都复制到From区。
GC垃圾回收机制详解
经过若干次YoungGC后,在From区和To区搬了游荡了几圈后,(Jvm是15次,因为美国是15岁成年)jvm就不耐烦了,既然你们还没挂掉,那就直接滚去(复制)老年代吧。
GC垃圾回收机制详解
老年代经过几次折腾,也扛不住了(空间被用完),那就来一次大扫除吧(FullGC),这是全量清除,会暂停程序运行的,所以如果频繁的FullGC,无疑就是程序断断续续的运行,会极大的影响它的性能,所以要合理的设置年轻代和老年代的大小 ,尽量减少FullGC的操作。

五.总结

根据GC的工作原理,我们可以通过一些技巧,让GC 运行的更有效率,更加符合程序的需要。一些小建议:
1.就是尽早的释放无用对象的引用。因为大的对象和数组比较占用内存,引用相对比较复杂,对于这些对象,GC对他们的回收效率一般比较低。所以如果程序允许,尽早将不用的对象赋值为null。这样可以让GC更有效率,减少无故的程序停止。
2.当程序有一定的等待时间,程序员可以手动执行System.gc(),通知GC运行,但是Java语言规范并不能保证GC一定会执行。使用增量式GC可以缩短Java程序的暂停时间。

看完三件事

如果你觉得这篇内容对你挺有启发,我想邀请你帮我三个小忙:
1.点赞,让更多的人也能看到这篇内容(收藏不点赞,都是耍流氓 -_-)
2.关注我,我会不定期分享原创知识。
3.也看看其它文章