JVM系列:GC总结

回收算法

我们知道,虚拟机栈、程序计数器和本地方法栈都是随线程生,随线程灭。线程结束时,内存也就自然释放了。但是Java堆和方法区则不一样,垃圾回收期关注的就是这部分区域。垃圾指的是死亡的对象所占据的堆空间。这里便涉及了一个关键的问题:如何辨别一个对象是存是亡?

  1. 引用计数算法
    给每一个对象添加一个计数器,每当有一个引用指向它时,计数器加一。当计引用失效时,计数器减一。如果一个对象的引用计数为零,那么该对象就变成了所谓的不可达对象,即可以被回收的。优点:效率很高、简单。缺点:很难解决对象之间相互循环引用的问题
  2. 可达性分析算法
    基本思想是通过称为GC Roots的对象作为起点,然后从该节点一次向下搜索,搜索走过的路径称为引用链,当一个对象到GC Roots没有引用链相连时,则证明此对象时不可用的。 一般可以作为GC Roots的对象有:
    • 虚拟机栈中引用的对象
    • 方法区中类静态属性引用的对象
    • 方法区中常量引用的对象
    • 本地方法栈中JNI引用的对象

当标记完所有的存活对象时,我们便可以进行死亡对象的回收工作了。主流的基础回收方式可分为三种。

1. 标记-清除算法

标记-清除算法是现代垃圾回收算法的思想基础。标记-清除算法将垃圾回收分为两个阶段:标记阶段和清除阶段。在标记阶段首先通过根节点,标记所有从根节点开始的可达对象。因此未被标记的对象就是未被引用的垃圾对象;然后在清除阶段,清除所有未被标记的对象。

但该方式效率低下,遍历所有所有对象,产生大量的不连续内存碎片。

2. 复制算法(新生代GC)

即把内存区域分为两等分,分别用两个指针 from 和 to 来维护,并且只是用 from 指针指向的内存区域来分配内存。当发生垃圾回收时,便把存活的对象复制到 to 指针指向的内存区域中,并且交换 from 指针和 to 指针的内容。复制这种回收方式同样能够解决内存碎片化的问题,但是它的缺点也极其明显,即堆空间的使用效率极其低下。

3. 标记整理算法(老年代GC)

复制算法在对象存活率较高时就要进行较多的复制操作,效率将会变低。所以老年代一般不会选用这种算法。根据老年代特点,提出标记-整理算法,在整理阶段首先对回收对象的清理,然后将存活的对象向一端移动。

即使一个对象在可达性分析算法中时不可达对象,也并非时非死不可,一个对象的真正死亡至少要经历两次标记过程。如果a对象在进行可达性分析后发现没有与GC Roots 相连接的引用链,那么它将会被第一次标记并且进行一次筛选。即判定是否有必要执行finalize方法,如果被判定有必要执行finalize,则会放入FQueue队列,并自动创建一个低优先级的finalize线程来执行释放操作。稍后GC将对队列中的对象进行第二次标记,如果在一个对象释放前被其他对象引用,则该对象会被移除FQueue队列。否则该对象就真正的被回收了。

注意:每个对象的finalize()方法只会被jvm调用一次,如果一个对象在第一次执行finalize()时候被拯救,在下次执行回收会直接对对象就行回收,将不会调用对象的finalize()方法。

回收方法区

方法区有垃圾回收,但是回收的效率低。方法区只要回收废弃的常量和无用的类。如果没有任何地方对此常量进行引用,则此常量就会被回收。

哪些是无用的类?

  1. java堆中不存在该类的任何实例。
  2. 加载该类的ClassLoader已经被回收。
  3. 该类的class对象没有任何地方被引用。
TLAB

通常来说,当我们调用 new 指令时,它会在 Eden 区中划出一块作为存储对象的内存。由于堆空间是线程共享的,因此直接在这里边划空间是需要进行同步的。论是使用哪种同步方案,即使上虚拟机使用的可能是CAS,仍然会影响内存的分配效率。为了解决这个问题,提出TLAB分配,即Thread Local Allocation Buffer。

TLAB是虚拟机在堆内存的eden划分出来的一块专用空间,是线程专属的。在虚拟机的TLAB功能启动的情况下,在线程初始化时,虚拟机会为每个线程分配一块TLAB空间,只给当前线程使用,这样每个线程都单独拥有一个空间,如果需要分配内存,就在自己的空间上分配,这样就不存在竞争的情况,可以大大提升分配效率。

在TLAB分配之后,并不影响对象的移动和回收,也就是说,虽然对象刚开始可能通过TLAB分配内存,存放在Eden区,但是还是会被垃圾回收或者被移到Survivor Space、Old Gen等。另外因为eden区域本身就不太大,而且TLAB空间的内存也非常小,默认情况下仅占有整个Eden空间的1%。可以通过设置-XX:+/-UseTLAB参数来指定是否开启TLAB分配。存在一些大对象是无法在TLAB直接分配。

卡表与写屏障

GC分类
  • Partial GC:指目标不是完整收集整个Java堆的垃圾收集

    • Minor GC/Young GC:指目标只是新生代的垃圾收集。
    • Major GC/Old GC:指目标只是老年代的垃圾收集。目前只有CMS收集器会有单独收集老年代的行为。
    • Mixed GC:指目标是收集整个新生代以及部分老年代的垃圾收集。目前只有G1收集器会有这种行为。
  • Full GC:收集整个Java堆和方法区的垃圾收集。

Young GC

HotSpot JVM把年轻代分为了三部分:1个Eden区和2个Survivor区,分别叫From和To。默认比例为8:1, 一般情况下,新创建的对象都会被分配到Eden区,一些大对象特殊处理。这当 Eden 区的空间耗尽了怎么办?这个时候 Java 虚拟机便会触发一次 Minor GC,来收集新生代的垃圾。存活下来的对象,则会被送到 Survivor 区。。对象在Survivor区中每熬过一次Minor GC,年龄就会增加1,当它的年龄增加到一定程度时,就会被移动到年老代中。

因为年轻代中的对象基本都是朝生夕死的,所以在年轻代的垃圾回收算法使用的是复制算法,复制算法的基本思想就是将内存分为两块,每次只用其中一块,当这一块内存用完,就将还活着的对象复制到另外一块上面。复制算法不会产生内存碎片。

在GC开始的时候,对象只会存在于Eden区和称为From的Survivor区,Survivor区To是空的。紧接着进行GC,Eden区中所有存活的对象都会被复制到To,而在From区中,仍存活的对象会根据他们的年龄值来决定去向。年龄达到一定值(可以通过-XX:MaxTenuringThreshold来设置)的对象会被移动到年老代中,没有达到阈值的对象会被复制到To区域。经过这次GC后,Eden区和From区已经被清空。这个时候,From和To会交换他们的角色,Minor GC会一直重复这样的过程,直到To区被填满,To区被填满之后,会将所有对象移动到年老代中。

卡表

Minor GC 的另外一个好处是不用对整个堆进行垃圾回收。但是,它却有一个问题,那就是老年代的对象可能引用新生代的对象。也就是说,在标记存活对象的时候,我们需要扫描老年代中的对象。

以 OpenJDK 中的 Parallel GC 为例,收集器通过卡表(Card Table) 记录老年代指向新生代的引用,所以Minor GC通过扫描Card Table就可以很快的识别老年代引用新生代。当完成所有脏卡的扫描之后,Java 虚拟机便会将所有脏卡的标识位清零。

什么时候进行GC?

当年轻中的eden区分配满的时候触发Young GC。注意Young GC中有部分存活对象会晋升到老年代,所以Young GC后old gen的占用量通常会有所升高。

当准备要触发一次Young GC时,如果发现统计数据说之前Young GC的平均晋升大小比目前老年代剩余的空间大,则不会触发Young GC而是转为触发Full GC,HotSpot VM的GC里,除了CMS的concurrent collection之外,其它能收集老年代的GC都会同时收集整个堆包括年轻代,所以不需要事先触发一次单独的Young GC。或者调用System.gc时,系统建议执行Full GC,但是不必然执行,老年代空间不足,方法区空间不足等。默认也是触发full GC。

Parallel Scavenge(-XX:+UseParallelGC)框架下,默认是在要触发Full GC前先执行一次Young GC,并且两次GC之间能让应用程序稍微运行一小下,以期降低Full GC的暂停时间(因为young GC会尽量清理了young gen的死对象,减少了full GC的工作量)。控制这个行为的VM参数是-XX:+ScavengeBeforeFullGC。这是HotSpot VM里的奇葩嗯。

CMS定时去检查老年代的使用量,当使用量超过了触发比例就会启动一次CMS GC,即对老年代做并发收集。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值