垃圾回收机制的一些总结

垃圾回收机制

哪些内存需要回收

由于程序计数器、虚拟机栈、本地方法栈的生命周期都跟随线程的生命周期,当线程销毁了,内存也就回收了,所以这几个区域不用过多地考虑内存回收。由于堆和方法区的内存都是动态分配的,而且是线程共享的,所以内存回收主要关注这部分区域。

如何判断对象为垃圾对象

1. 引用计数法

对象添加一个引用计数器,每当有一个地方引用它时,计数器值就加1,如果引用失效,计数器值减1,所以当该计数器的值为0时,就表示该对象可以被回收了。但是存在两个对象之间相互循环引用的问题。

两个对象之间相互循环,计数器不为0,但是应该被回收。

2. 可达性分析法

通过一系列的称为“GC Roots”的对象作为起始点,从这些节点开始向下搜索,搜索的路径称为引用链,当一个对象到“GC Roots”没有任何引用链相连的话,也就是GC Roots到这个对象不可达时,证明此对象已经不可用,可以被回收了。
作为GC Roots的对象

  • 虚拟机栈
  • 方法区的类属性所应用的对象
  • 方法区中常量所应用的对象
  • 本地方法栈中引用的对象

二次标记的概念

在可达性分析算法中被判断是对象不可达时不一定会被垃圾回收机制回收,因为要真正宣告一个对象的死亡,必须经历两次标记的过程。如果发现对象不可达时,将会进行第一次标记,此时如果该对象调用了finalize()方法,那么这个对象会被放置在一个叫F-Queue的队列之中,如果在此队列中该对象没有成功拯救自己(拯救自己的方法是该对象有没有被重新引用),那么GC就会对F-Queue队列中的对象进行小规模的第二次标记,一旦被第二次标记的对象,将会被移除队列并等待被GC回收,所以finalize()方法是对象逃脱死亡命运的最后一次机会。

如何回收

回收策略

1. 标记-清除算法

首先标记出需要回收的对象,在标记完成后进行统一的回收(标记即二次标记的过程)。此算法有两个不足:一是效率问题,标记和清除两个过程效率都不高;二是空间问题,标记清除后会产生大量不连续的内存碎片,内存空间碎片太多的话会导致以后程序在运行中想要分配较大对象的时候,无法找到一块连续的内存空间而导致不得不进行又一次的GC回收(后续的垃圾回收算法都是基于此算法进行改进的)。

2. 复制算法

把内存按容量划分为大小相等的两块区域,每次只使用其中的一块,当这一块的内存空间用完了,就把还存活的对象复制到另一块内存中去,然后把已经使用的过的内存空间一次性清理掉。这样每次都是对半个内存区域进行GC回收,并不会产生内存碎片,但是代价是把内存缩小了一半,效率比较低

改进:
新生代内存分为了三份,Eden区和2块Survivor区,一般Sun的JVM会将Eden区和Survivor区的比例调为8:1,保证有一块Survivor区是空闲的,这样,在垃圾回收的时候,将不需要进行回收的对象放在空闲的Survivor区,然后将Eden区和第一块Survivor区进行完全清理,这样有一个问题,就是如果第二块Survivor区的空间不够大怎么办?这个时候,就需要当Survivor区不够用的时候,暂时借持久代的内存用一下。此算法适用于新生代。

3. 标记-整理算法

标记算法一样,区别是清除的时候会把所有存活的对象向一端移动(向上和向左),然后清除掉端边界以外的内存。

  • 适用于需要清除对象较小的区域,如老年代;
4. 分代收集算法

根据对象存活周期的不同将内存划分为几块(新生代或老生代),然后根据每个年代的特点采用最合适的收集算法。比如在新生代中,每次都有大量对象死去,就选择复制算法;而在老生代中对象的生存率高,没有额外的空间为它进行分配担保,所以采用标记—清除算法或者标记—整理算法来进行回收。

垃圾回收器

1. Serial

最基本,发展最悠久的,串行的垃圾收集器,为单线程环境设计,可能会产生较长停顿;新生代复制算法,老年代标记-整理算法;

串行垃圾回收器在进行垃圾回收时,它会持有所有应用程序的线程,冻结所有应用程序线程,使用单个垃圾回收线程来进行垃圾回收工作。

  • 使用方法:-XX:+UseSerialGC 串联收集
2. ParNew

ParNew收集器其实就是Serial收集器的多线程版本;新生代并行,老年代串行;新生代复制算法、老年代标记-整理;

  • 使用方法:-XX:+UseParNewGC ParNew收集器
3.Parallel收集器

Parallel Scavenge收集器类似ParNew收集器,Parallel收集器更关注系统的吞吐量。可以通过参数来打开自适应调节策略,虚拟机会根据当前系统的运行情况收集性能监控信息,动态调整这些参数以提供最合适停顿时间最大的吞吐量;也可以通过参数控制GC的时间不大于多少毫秒或者比例。

  • 吞吐量 = (执行用户代码时间)/(执行用户代码时间+GC时间)
  • 和用户体验优先的场景不一样,主要是用在保证后台服务尽快运行完成,而不是提高响应速度。适合在后台运算,又不需要太多交互的任务。
4. CMS(Concurrent Mark Sweep)

工作过程:

  • 初始标记
  • 并发标记
  • 重新标记
  • 并发清理
    优点:
  • 并发收集
  • 低停顿
    高并发、低停顿,追求最短GC回收停顿时间,cpu占用比较高,响应时间快,停顿时间短,多核cpu追求高响应时间的选择

空间的碎片化严重

5. G1收集器(Garbage First)

优点:

  • 并行与并发
  • 分代收集
  • 空间整合
  • 可预测的停顿
    步骤:
  • 初始标记
  • 并发标记
  • 最终标记
  • 筛选回收

何时回收

Minor GC 从年轻代回收内存

触发条件:

  1. Eden区域满
  2. 新创建的对象大小>Eden剩余空间
Major GC/Full GC 清理整个堆空间,包括年轻代和老年代
  1. 每次晋升到老年代的对象>老年代剩余对象空间
  2. MinorGc后存活的对象超过了老年代剩余对象空间
  3. 运行System.gc()
  4. 堆内存分配很大的对象

大对象直接进入老年代,避免在Eden区及两个Survivor区之间发生大量的内存复制(复习一下:新生代采用复制算法收集内存)。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值