Java虚拟机(三)GC

目录

 

一、GC简介

二、回收对象的判断

三、回收算法

四、分代收集算法

五、常用垃圾收集器


一、GC简介

Java中对象的回收是JVM做的,不需要手动释放。JVM对于哪些对象要回收,何时回收,如何回收,都有特定的规则,也在不断地优化和演进,这些问题,也就是GC垃圾回收处理的内容。

二、回收对象的判断

1、引用计数法

判断对象是否可以回收,最直接的方法就是引用计数法。原理是每个对象有一个计数器,如果出现一个该对象的引用,则计数器+1;如引用失效,则计数器-1;当计数器为0时,该对象废弃。

引用计数法解决不了循环引用问题,并且如果引用的类型很多,一个计数器解决不了。

2、可达性分析算法

可达性分析算法通过一些列名为GC ROOT的节点开始搜索,走过的路径成为引用链,不在引用链上的节点对象,被认为是可回收对象。这种算法规避了引用计数法的问题。

那么哪些对象可以作为GC ROOT呢?一般下列对象可以:

  • 虚拟机栈(栈桢中的本地变量表)中的引用的对象
  • 本地方法栈中JNI引用的对象。
  • 方法区中常量引用的对象,
  • 方法区中类静态属性引用的对象等

选择GC ROOT的依据就是,无法通过其他的对象引用找到,并且通过这些对象可以找到其他被引用的对象。

3、两次标记

JVM中并不是可达性分析算法得到的对象就直接释放,谨慎起见,实际是经过了两次标记:

第一次标记:对于一个没有其他引用的对象,筛选该对象是否有必要执行finalize()方法,如果没有执行必要,则意味可直接回收。(筛选依据:如果没覆写finalize()方法或该方法已被调用过,则直接回收了;因为finalize方法只能被执行一次)。 

第二次标记:如果被筛选判定位有必要执行,则会放入FQueue队列,并自动创建一个低优先级的finalize线程来执行释放操作。如果在一个对象释放前被其他对象引用,则该对象会被移除FQueue队列。 

三、回收算法

基本的回收算法有3种:标记-清除算法、标记-复制算法、标记-整理算法。

1)标记-清除算法

思路:先标记(标记的过程其实就是可达性分析),再清除。有个free-list记录所有空闲区域及区域的大小。

缺点:一是扫两遍效率低;二是处理后碎片太多,分配大内存困难。

注:之所以要先标记再清除是因为在标记全部完成前无法确定对象是否可能被引用到,因此不能标记一个清除一个。

2)标记-复制算法

思路:内存分为大小相等的两块,一块用完后将存活的对象搬到另一块。

缺点:可用内存缩减为原来的一半。

3)标记-整理算法

思路:将存留的对象而是向一端移动,然后回收边界以外内存。

缺点:由于需要将对象复制到另外的地方,然后修改指向这些对象的引用,因此效率较低。

四、分代收集算法

分代收集算法是目前JVM实际在用的收集算法,它是基于上述基本算法综合实现的。

JVM将堆内存分为3部分:新生代、旧生代、持久代,不同内存分类有不同的收集算法。

1)新生代GC(MinorGC)

触发条件:Eden区空间不足。

描述:将Eden区和一个survivor区的数据移到另一个survivor区。

算法:Eden中的对象大部分存活时间很短,且GC频繁触发,因此用复制算法。

2)老年代GC(MajorGC)

触发条件:老年代空间不足;执行System.gc()方法;(之前持久代也在这里,JDK1.8后改掉了)

算法:对象存活率高,不能用占空间的复制算法,用标记-清理或标记-整理算法。

3)FullGC

描述:清理所有区域。

触发条件:

  • 调用System.gc()时,系统建议执行Full GC,但是不必然执行;
  • 老年代空间不足;
  • 方法区空间不足;
  • 通过Minor GC后进入老年代的平均大小大于老年代的可用内存;
  • 由Eden区、From Space区向To Space区复制时,对象大小大于To Space可用内存,则把该对象转存到老年代,且老年代的可用内存小于该对象大小。

五、常用垃圾收集器

常用新生代的垃圾收集器:

  • Serial收集器:单线程收集器,工作时必须暂停其他所有的工作线程,需要stoptheworld。
  • ParNew收集器:Serial的多线程版本,同样需要stoptheworld。
  • Parallel Scavenge收集器:多线程收集器,可控制吞吐量,即执行用户线程的时间占CPU总运行时间的比例。

常用老年代的垃圾收集器:

  • Serial Old收集器:Serial老年代版本,使用标记整理算法;
  • Parallel Old收集器:Parallel Scavenge收集器的老年代版本,使用标记整理算法。
  • CMS收集器:真正的并发收集器,目标是尽量减少应用的暂停时间,减少full gc发生的几率,利用和应用程序线程并发的垃圾回收线程来标记清除年老代。

最后,有一种做新生代老年代都可用的收集器:G1回收器,其设计目的是处理超大堆的处理效率。

猜你喜欢

转载自blog.csdn.net/ss1300460973/article/details/85547753