垃圾回收机制

在运行过程中没有任何指针指向的对象是垃圾。

垃圾标记阶段:对象存活判断

需要区分出内存中哪些是存活对象,哪些是已经死亡的对象,这个过程称为垃圾标记阶段。

两种方式:引用计数算法和可达性分析算法

  • 引用计数算法 对每个对象保存一个整型的引用计数器属性。用于记录对象被引用的情况。
  • 可达性分析算法,解决了引用计数算法中循环引用的问题,防止内存泄漏的发生。按照从上至下的方式搜索被根对象集合所连接的目标对象是否可达。

在java语言中,GCROOTs包括以下几类元素:

  • 虚拟机栈中引用的对象
  • 本地方法栈中引用的对象
  • 方法区中静态属性引用的对象
  • synchronized持有的对象

 

finalization 机制

作用:用于在对象被回收时进行资源回收

finalize()方法不能主动调用,原因如下:

  • 在finalize() 时可能导致对象复活(这取决于对象能否与引用链取得联系)
  • finalize()方法的执行时间是没有保障的,完全由gc线程决定
  • 可能严重影响gc性能

一个对象的finalize()方法只会执行一次

垃圾清除阶段:清除算法

  • 标记清除

从根节点开始遍历所有可达的对象,如果发现没有标记的对象,将其回收

缺点:效率不高,产生内存碎片,需要维护空闲列表

  • 复制

将活着的内存空间分为2块,每次只使用其中的一块,在垃圾回收时将活着的对象复制到未被使用的内存块中。

优点:简单高效,没有碎片

缺点:需要2倍的内存空间

特别适用于垃圾对象很多,存活对象很少的情景,如young区的s0 和s1

  • 标记压缩

第一阶段和标记清除算法一样

第二阶段将所有的存活对象压到内存的一端,按顺序排放。

优点:没有内存碎片,没有内存减半

缺点:效率低,需要移动对象。

 

指针碰撞:如果内存已用和未用的部分各占一边,当新对象分配内存时,只需要修改指针到第一个空闲位置上。

 

针对不同生命周期的对象可以采取不同的收集方式。

 

System.gc()

提醒 jvm的垃圾回收器执行gc,但是不确定是否马上执行gc.

stop the world

gc事件发生过程中,会产生应用程序的停顿。

safepoint 安全点

程序执行时并非在所有地方都能停下来gc,只有在特定位置才开始gc,这些位置被称为检查点。

 

引用

  • 强引用 引用赋值,只要强引用关系存在,垃圾回收器就永远不会回收掉被引用的对象。
  • 软引用 在即将发生内存溢出时,将这些对象列入垃圾回收器进行2次回收。如果这次回收之后还没有足够的内存,才会抛出异常
  • 弱引用 被弱引用关联的对象只能生存到下一次垃圾回收之前。当垃圾收集器工作时,无论内存空间是否足够,都会回收。
  • 虚引用 在对象被收集器回收时收到一个系统通知。

垃圾回收器分类

串行与并行回收: 在同一个时间段内只允许有一个CPU用于执行垃圾回收工作

并发式与独占式: 并发式垃圾回收器与应用线程交替工作,以尽可能减少应用程序停顿的时间。独占式垃圾回收器:一旦运行,就停止应用中所有用户线程。

按碎片的处理方式划分:压缩式和非压缩式

按工作的内存区间划分:年轻代回收器和老年代回收器。

评价gc的性能指标

吞吐量:运行用户代码的时间占总运行时间的比例

暂停时间:执行垃圾收集时,程序的工作线程被暂停的时间

内存占用:java堆区所占的内存大小

对于一个交互时程序低暂停时间是非常必要的。但是会频繁地执行垃圾回收。

7款经典的垃圾回收器

串行垃圾回收器:Serial、Serial old

并行回收器:ParNew,Parallel Scavenge 、Parallel Old

并发回收器:CMS、G1

垃圾收集器与垃圾分代的关系

新生代收集器:Serial, ParNew, Parallel Scavenge

老年代收集器:Serial Old,Parallel old ,cms

整堆收集:G1

 

Serial回收器

采用复制算法,串行回收,stw。只会使用一个CPU或一条收集线程去完成垃圾收集工作。在进行垃圾回收时,必须暂停其他所有线程。

简单而高效,限定单核CPU。

Serila Old 与 Serial 配合工作

Parnew回收器

是Serial回收器的多线程版本。采用并行回收的方式,复制算法。

对于新生代,回收次数频繁,使用并行方式高效。但是在单个CPU的环境下,ParNew收集器并不比Serial收集器高。

对于老年代,回收次数少,使用串行方式节省资源。

除了Serial外,目前只有Parnew gc 能与 CMs配合工作。

Parallel Scavenge 回收器

吞吐量优先,复制算法,并行回收,stw机制

适合在后台运算而不需要太多交互的任务。

采用标记压缩算法,并行回收和stw机制。

新生代采用复制算法,暂停所有用户线程。老年代采用标记压缩算法。

在吞吐量优先的场景中,Parallel 一般和 Parallel old 组合。java8默认是此收集器。

CMS 回收器

实现了让垃圾收集线程与用户线程同时工作。采用了标记清除算法。

  • 初始标记 标记出GC Roots能直接关联到的对象,stw,速度非常快
  • 并发标记 遍历整个对象图,耗时较长,但是不需要停顿用户线程
  • 重新标记 修正并发标记期间因用户线程执行而导致的标记产生变动的对象,stw
  • 并发清除 删除已经标记的死亡的对象,释放内存

由于最耗费时间的并发标记与并发清除阶段都不需要暂停工作,所以整体的回收是低停顿的。

cms 算法采用的是标记清除算法,会产生内存碎片

优点:并发收集,低延迟

缺点:

  • 产生内存碎片
  • 并发阶段占用一部分线程导致用户程序变慢总吞吐量降低。
  • 产生浮动垃圾 在重新标记阶段会重新判断已经标记为垃圾的对象是不是垃圾。无法判断原来不是垃圾的对象会不会变成垃圾。

为什么不使用标记压缩算法?

采用压缩算法会影响用户线程的执行

G1回收器

它是一个并行回收器,把堆内存分为很多不相关的区域,每次根据允许收集的时间,优先回收价值最大的region。是jdk9以后的默认垃圾回收器。

特点:

空间整合:G1 将内存划分为一个个region。内存的回收是以region为基本单位的,region之间是复制算法,但整体上可以看作标记压缩算法。

可预测的停顿时间模型:G1跟踪region里面垃圾堆积的价值大小,在后台维护一个列表,每次根据允许收集的时间,优先回收价值最大的region

缺点:占用内存和负载高

G1在大内存上可以发挥优势,适用于大内存,多处理器的机器。

 

化整为零:G1将java堆划分为2048个region块。 所有region大小相同,但年轻代与老年代不再物理隔离。G1还设置了humongous区域存储大对象。

 

G1回收器垃圾回收的过程:

  1. 年轻代GC
  2. 年轻代 GC+并发标记过程
  3. 混合回收

Remember Set 记忆集:

用来解决一个对象被不同区域引用的问题。

一个region 区域不可能是孤立的。其中的对象有可能被其他区域所引用。如果全部扫描非常浪费资源。于是出现了Remember Set。

每个region都有一个Remember Set。这样在扫描时可以保证不进行全局扫描也不会有遗漏。

 

 

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值