Java GC
GC需要的内存区域
我们的内存垃圾回收主要集中于 java 堆和方法区中,在程序运行期间,这部分内存的分配和使用都是动态的
Minor GC
从年轻代空间(包括 Eden 和 Survivor 区域)回收内存被称为 Minor GC
触发
- 当Eden区满时,触发Minor GC
Full GC
清理整个堆空间—包括年轻代和永久代
触发
- 调用System.gc时,系统建议执行Full GC,但是不必然执行
- 老年代空间不足
- 方法去空间不足
- 通过Minor GC后进入老年代的平均大小大于老年代的可用内存
判断对象可回收
- 引用计数
每个对象有一个引用计数属性,新增一个引用时计数加1,引用释放时计数减1,计数为0时可以回收。此方法简单,无法解决对象相互循环引用的问题
- 可达性分析
从GC Roots开始向下搜索,搜索所走过的路径称为引用链。当一个对象到GC Roots没有任何引用链相连时,则证明此对象是不可用的。不可达对象。
GC Roots包括:
- 虚拟机栈中引用的对象
- 方法区中类静态属性实体引用的对象
- 方法区中常量引用的对象
- 本地方法栈中JNI引用的对象
GC算法
标记清除
为每个对象存储一个标记位,记录对象的状态(活着或是死亡)。分为两个阶段,一个是标记阶段,这个阶段内,为每个对象更新标记位,检查对象是否死亡;第二个阶段是清除阶段,该阶段对死亡的对象进行清除,执行 GC 操作
优点
- 不需要额外空间
- 每个活着的对象的引用只需要找到一个即可,找到一个就可以判断它为活的
缺点
- 产生过多的内存碎片
- 效率低(每个活着的对象都要在标记阶段遍历一遍)
标记压缩
标记清除算法的改进版。同样,在标记阶段,该算法也将所有对象标记为存活和死亡两种状态;不同的是,在第二个阶段,该算法并没有直接对死亡的对象进行清理,而是将所有存活的对象整理一下,放到另一处空间,然后把剩下的所有对象全部清除。这样就达到了标记-整理的目的
优点
- 该算法不会像标记-清除算法那样产生大量的碎片空间
缺点 - 如果存活的对象过多,整理阶段将会执行较多复制操作,导致算法效率降低
复制算法
该算法将内存平均分成两部分,然后每次只使用其中的一部分,当这部分内存满的时候,将内存中所有存活的对象复制到另一个内存中,然后将之前的内存清空,只使用这部分内存,循环下去。
注意:
这个算法与标记压缩算法的区别在于,该算法不是在同一个区域复制,而是将所有存活的对象复制到另一个区域内
优点
- 简单,不会产生内存碎片
缺点 - 每次GC时都会有一半内存是空的,浪费空间
分代收集算法
现在的虚拟机垃圾收集大多采用这种方式,它根据对象的生存周期,将堆分为新生代(Young)和老年代(Tenure)。在新生代中,由于对象生存期短,每次回收都会有大量对象死去,那么这时就采用复制算法。老年代里的对象存活率较高,没有额外的空间进行分配担保,所以可以使用标记-整理 或者 标记-清除
具体过程
新生代(Young)分为Eden区,From区与To区
当系统创建一个对象的时候,总是在Eden区操作,当这个区满了,那么就会触发一次YoungGC,也就是年轻代的垃圾回收。一般来说这时候不是所有的对象都没用了,所以就会把还能用的对象复制到From区
这样整个Eden区就被清理干净了,可以继续创建新的对象,当Eden区再次被用完,就再触发一次YoungGC,然后呢,注意,这个时候跟刚才稍稍有点区别。这次触发YoungGC后,会将Eden区与From区还在被使用的对象复制到To区
再下一次YoungGC的时候,则是将Eden区与To区中的还在被使用的对象复制到From区
经过若干次YoungGC后,有些对象在From与To之间来回游荡,这时候From区与To区亮出了底线(阈值),这些家伙要是到现在还没挂掉,对不起,一起滚到(复制)老年代吧
老年代经过这么几次折腾,也就扛不住了(空间被用完),好,那就来次集体大扫除(Full GC),也就是全量回收。如果Full GC使用太频繁的话,无疑会对系统性能产生很大的影响。所以要合理设置年轻代与老年代的大小,尽量减少Full GC的操作
收集器
转自
https://blog.csdn.net/laomo_bible/article/details/83112622