垃圾回收

垃圾回收

1. 判断对象是否为垃圾对象

1.1 引用计数法

给对象添加一个引用计数器,每当有一个地方引用它,计数器值加1;引用失效时,计数器值减1;任何计数器为0的对象就是不可能被使用的。

优点:实现简单,判定效率高
缺点:很难解决对象之间相互循环引用的问题.

1.2 可达性分析法

起始点:GC Roots
从GC Roots开始向下搜索,搜索走过的路径称为引用链,当一个对象到GC Roots没有任何引用链相连,就证明这个对象是不可引用的。

可作为GC Roots的对象:
1.虚拟机栈中引用的对象
2.方法区中的类静态属性引用的对象
3.方法区中常量引用的对象
4.本地方法栈中JNI(即一般所说的Native方法)引用的对象

1.3 不可引用与死亡对象

不可引用对象到死亡对象,至少要经过两次标记过程
1.对象在可达性分析后到GC Roots没有任何引用链相连,那将进行第一次标记和进行一次筛选。筛选条件为是否有必要执行finalize()方法。当对象没有覆盖finalize()方法,或者finalize()方法已经被虚拟机调用过,这两种情况将被视为“没有必要执行”;如果对象被判定为有必要执行finalize()方法,会将对象放置到F-Queue的队列中。

2 稍后GC对F-Queue中的对象进行第二次小规模的标记,这时对象只要重新与引用链上的任何一个对象建立关联,即可将自己移除“即将回收”的集合;否则将被回收;

2.如何回收

2.1 垃圾回收算法

2.1.1 标记-清除算法

算法分为标记和清除两个阶段,首先标记出需要清除的对象,在标记完成后统一回收所有被标记的对象。
效率问题:效率不高
空间问题:清除后会产生大量不连续的内存碎片。

2.1.2复制算法

算法思想:将内存按容量大小划分为大小相等的两块。使用其中的一块,当这块用完后,将存活的对象复制到另一快上去,把已使用的内存空间一次清理掉。
缺点:浪费空间
改进:分为一块较大的Eden和两块较小的Survivor空间(Eden:Survivor 大约为8:1)
在这里插入图片描述

每次使用Eden和一块Survivor,回收时把还存活的对象一次复制到另一块Survivor中,清理掉之前的Eden和Survivor。
注:Survivor空间不够时,用老年代进行分配担保

堆 { 新 生 代 { E d e n 伊 甸 园 S u r v i v o r 存 活 区 T e n u r e d G e n 老 年 代 堆\begin{cases} 新生代\begin{cases} Eden伊甸园\\Survivor存活区\\Tenured Gen \end{cases} \\老年代 \end{cases} EdenSurvivorTenuredGen

2.1.3 标记-整理算法

对要清除的对象进行标记,让存活的对象向一端移动,直接清除存活区域以外的内存。

2.1.4分代收集算法

根据对象存活周期,把内存划分为几块,根据各个年代的对象采用最适当的算法。

2.2 垃圾回收器

收集算法为内存回收的方法论,垃圾收集器为内存回收的具体实现

2.2.1 Serial

在这里插入图片描述
Serial 翻译为串行,也就是说它以串行的方式执行。
它是单线程的收集器,只会使用一个线程进行垃圾收集工作。
它的优点是简单高效,在单个 CPU 环境下,由于没有线程交互的开销,因此拥有最高的单线程收集效率。
它是 Client 场景下的默认新生代收集器,因为在该场景下内存一般来说不会很大。它收集一两百兆垃圾的停顿时间可以控制在一百多毫秒以内,只要不是太频繁,这点停顿时间是可以接受的。
单线程:会停止其他所有工作线程

2.2.2 Parnew

在这里插入图片描述

它是 Serial 收集器的多线程版本。
它是 Server 场景下默认的新生代收集器,除了性能原因外,主要是因为除了 Serial 收集器,只有它能与 CMS 收集器配合使用。

2.2.3 Parallel Scavenge

新生代收集器 复制算法 并行多线程

关注点:尽可能缩短垃圾收集时用户线程的停顿时间。

吞吐量:运行用户代码时间/(运行用户代码时间+垃圾收集总时间)

两个参数:
− X X : M a x G C P a u s e M i l l i s -XX:MaxGCPauseMillis XX:MaxGCPauseMillis:控制最大垃圾回收停顿时间
− X X : G C T i m e R a t i o -XX:GCTimeRatio XX:GCTimeRatio:设置垃圾吞吐量大小

Parallel Scavenge达到一个可控制的吞吐量,GC停顿时间的缩短是以牺牲吞吐量和新生代空间来作为代价的

2.2.4 Serial Old 收集器

在这里插入图片描述
是 Serial 收集器的老年代版本,也是给 Client 场景下的虚拟机使用。如果用在 Server 场景下,它有两大用途:
在 JDK 1.5 以及之前版本(Parallel Old 诞生以前)中与 Parallel Scavenge 收集器搭配使用。
作为 CMS 收集器的后备预案,在并发收集发生 Concurrent Mode Failure 时使用。

2.2.5 Parallel Old 收集器

在这里插入图片描述
是 Parallel Scavenge 收集器的老年代版本。
在注重吞吐量以及 CPU 资源敏感的场合,都可以优先考虑 Parallel Scavenge 加 Parallel Old 收集器。

2.2.6 Cms

在这里插入图片描述

CMS(Concurrent Mark Sweep),Mark Sweep 指的是标记 - 清除算法。

分为以下四个流程:

运 作 过 程 { 初 始 标 记 : 仅 仅 只 是 标 记 一 下 G C R o o t s 能 直 接 关 联 到 的 对 象 , 速 度 很 快 , 需 要 停 顿 。 并 发 标 记 : 进 行 G C R o o t s T r a c i n g 的 过 程 , 它 在 整 个 回 收 过 程 中 耗 时 最 长 , 不 需 要 停 顿 。 重 新 标 记 : 为 了 修 正 并 发 标 记 期 间 因 用 户 程 序 继 续 运 作 而 导 致 标 记 产 生 变 动 的 那 一 部 分 对 象 的 标 记 记 录 , 需 要 停 顿 。 并 非 清 除 : 不 需 要 停 顿 。 运作过程\begin{cases} 初始标记:仅仅只是标记一下 GC Roots 能直接关联到的对象,速度很快,需要停顿。\\ 并发标记:进行 GC Roots Tracing 的过程,它在整个回收过程中耗时最长,不需要停顿。\\ 重新标记:为了修正并发标记期间因用户程序继续运作而导致标记产生变动的那一部分对象的标记记录,需要停顿。\\ 并非清除:不需要停顿。\end{cases} GCRootsGCRootsTracing
在整个过程中耗时最长的并发标记和并发清除过程中,收集器线程都可以与用户线程一起工作,不需要进行停顿。

具有以下缺点:
吞吐量低:低停顿时间是以牺牲吞吐量为代价的,导致 CPU 利用率不够高。
无法处理浮动垃圾,可能出现 Concurrent Mode Failure。浮动垃圾是指并发清除阶段由于用户线程继续运行而产生的垃圾,这部分垃圾只能到下一次 GC 时才能进行回收。由于浮动垃圾的存在,因此需要预留出一部分内存,意味着 CMS 收集不能像其它收集器那样等待老年代快满的时候再回收。如果预留的内存不够存放浮动垃圾,就会出现 Concurrent Mode Failure,这时虚拟机将临时启用 Serial Old 来替代 CMS。
标记 - 清除算法导致的空间碎片,往往出现老年代空间剩余,但无法找到足够大连续空间来分配当前对象,不得不提前触发一次 Full GC。

2.2.7 G1

特 点 { 并 行 与 并 发 : 能 充 分 利 用 多 核 C P U 来 缩 短 停 顿 时 间 分 代 收 集 : 和 其 他 收 集 器 一 样 , 保 留 分 代 收 集 概 念 空 间 整 合 : 整 体 上 基 于 " 标 记 − 整 理 " 算 法 , 局 部 上 基 于 " 复 制 " 算 法 。 运 作 期 间 不 会 产 生 内 存 空 间 碎 片 可 预 测 的 停 顿 : 能 够 建 立 可 预 测 的 时 间 停 顿 模 型 , 让 使 用 者 明 确 指 定 停 顿 时 间 特点\begin{cases} 并行与并发:能充分利用多核CPU来缩短停顿时间\\ 分代收集:和其他收集器一样,保留分代收集概念\\ 空间整合:整体上基于"标记-整理"算法,局部上基于"复制"算法。运作期间不会产生内存空间碎片\\ 可预测的停顿: 能够建立可预测的时间停顿模型,让使用者明确指定停顿时间\end{cases} CPU""""使
G1保留了新生代和老年代的概念,但新生代和老年代不再是物理隔离的,它将整个Java堆划分为多个大小相等的独立区域(Region)。G1跟踪各个Region里面垃圾堆积的价值大小,后台维护一个优先列表,根据允许的时间,优先回收价值大的Region。

3 内存分配和回收策略

Java中的自动内存管理可以最终归结为自动化的解决了两个问题:给对象的分配内存回收分配给对象的内存

3.1 Minor GC 和 Full GC

Minor GC:回收新生代,因为新生代对象存活时间很短,因此 Minor GC 会频繁执行,执行的速度一般也会比较快。
Full GC:回收老年代和新生代,老年代对象其存活时间长,因此 Full GC 很少执行,执行速度会比 Minor GC 慢很多

3.2内存分配策略

1.对象优先在Eden分配
大多数情况下,对象在新生代 Eden 上分配,当 Eden 空间不够时,发起 Minor GC。

2.大对象直接进入老年代
大对象是指需要连续内存空间的对象,最典型的大对象是那种很长的字符串以及数组。
经常出现大对象会提前触发垃圾收集以获取足够的连续空间分配给大对象。
-XX:PretenureSizeThreshold 设定参数,大于这个参数,对象在老年代分配。

3.长期存活的对象进入老年代
Eden出生经过一次Minor GC后仍然存活,并且能加入Survivor容纳,将移动到Survivor,Age=1
对象每次在Survivor区中经过一次Minor GC,Age+1
− X X : M a x T e n u r i n g T h r e s h o l d -XX:MaxTenuringThreshold XX:MaxTenuringThreshold设置晋升老年代的阈值。

4.动态对象年龄判定
虚拟机并不是永远要求对象的年龄必须达到 MaxTenuringThreshold 才能晋升老年代,如果在 Survivor 中相同年龄所有对象大小的总和大于 Survivor 空间的一半,则年龄大于或等于该年龄的对象可以直接进入老年代,无需等到 MaxTenuringThreshold 中要求的年龄。

5.空间分配担保
在发生 Minor GC 之前,虚拟机先检查老年代最大可用的连续空间是否大于新生代所有对象总空间,如果条件成立的话,那么 Minor GC 可以确认是安全的。
如果不成立的话虚拟机会查看 HandlePromotionFailure 的值是否允许担保失败,如果允许那么就会继续检查老年代最大可用的连续空间是否大于历次晋升到老年代对象的平均大小,如果大于,将尝试着进行一次 Minor GC;如果小于,或者 HandlePromotionFailure 的值不允许冒险,那么就要进行一次 Full GC。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值