Java虚拟机 - 垃圾回收

JVM垃圾回收

柯南语录:比你厉害的人都在努力,你还有什么理由不努力。

上一篇:Java虚拟机 - 运行时数据区域

垃圾收集器(Garbage Collection,简称GC),垃圾收集需要完成的三件事情:
·哪些内存需要回收?
·什么时候回收?
·如何回收?

1. 那如何判断对象是否存活呢?

在堆里面存放着Java世界中几乎所有的对象实例,垃圾收集器在对堆进行回收前,
第一件事情就是要确定这些对象之中哪些还“存活”着,哪些已经“死去”(“死去”即不可能再被任何途径使用的对象)了。

1.1. 引用计数法

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

但是,在Java领域,至少主流的Java虚拟机里面都没有选用引用计数算法来管理内存
原因:引用计数很难解决对象之间相互循环引用的问题

1.2. 可达性分析算法(Java虚拟机采用)

Java虚拟机中的垃圾回收器采用可达性分析来探索所有存活的对象
扫描堆中的对象,看是否能够沿着GC Root对象为起点的引用链找到该对象,找不到,表示可以回收

在这里插入图片描述
固定可作为GC Roots的对象包括以下几种:
(1)在虚拟机栈(栈帧中的本地变量表)中引用的对象,例如:参数、局部变量、临时变量。
(2)在方法区中类静态属性引用的对象,例如:静态变量。
(3)在方法区中常量引用的对象,例如:字符串常量池(String Table)里的引用 。
(4)在本地方法栈中Native方法引用的对象
(5)Java虚拟机内部的引用,例如:基本数据类型对应的Class对象,还有系统类加载器。
(6)所有被同步锁(synchronized关键字)持有的对象。

2. 引用

引用分为强引用(Strongly Reference)、软引用(Soft Reference)、弱引用(Weak Reference)、虚引用(Phantorm Reference),这四种引用强度依次逐渐减弱。

2.1. 强引用

类似Object obj = new Object() 这种引用关系, 无论任何情况下,
只要强引用关系还在,垃圾收集器就永远不会回收掉被引用的对象

2.2. 软引用

用来描述一些还有用,但非必须的对象。只被软引用关联着的对象,
在系统将要发生内存溢出异常前,会把这些对象列进回收范围之中进行第二次回收。

2.3. 弱引用

弱引用用来描述非必须对象,强度比软引用弱些。
被弱引用关联的对象只能生存到下一次垃圾收集发生为止。
无论当前内存是否足够,都回回收只被弱引用关联的对象。

2.4. 虚引用

也称”幽灵引用“或者”幻影引用“,是最弱的一种引用关系。
一个对象是否有虚引用的存在,完全不会对其生存时间构成影响,也无法通过虚引用来取得一个对象实例。
为一个对象设置虚引用关联的唯一目的只是为了能在这个对象被收集器回收时收到一个系统通知。

3. 回收方法区

方法区的垃圾收集主要回收两部分内容:
(1)废弃的变量
(2)不再使用的类型
判定一个常量是否“废弃”还是相对简单,而要判定一个类型是否属于“不再被使用的类”的条件就需要同时满足三个条件:
(1)该类所有的实例都已经被回收
(2)加载该类的类加载器都已经被回收
(3)该类对应的java.lang.Class对象没有在任何地方被引用

Java虚拟机被允许对满足上述三个条件的无用类进行回收,这里说的仅仅是“被允许”,而并不是和对象一样,没有引用了就必然会回收。
在大量使用反射、动态代理、CGLib等字节码框架,动态生成JSP以及OSGi这类频繁自定义类加载器的场景中,通常都需要Java虚拟机具备类型卸载的能力,以保证不会对方法区造成过大的内存压力。

4. 垃圾收集算法

4.1. 分代收集理论

设计原则:收集器应该将Java堆划分出不同的区域,然后将回收对象依据其年龄(年龄即对象熬过垃圾收集过程的次数)分配到不同的区域之中存储。
Java堆划分出不同的区域之后,垃圾收集器才可以每次只回收其中某一个或者某些部分的区域
-因而才有了“Minor GC”“Major GC”“Full GC”这样的回收类型的划分
于是有了“标记-复制算法”“标记-清除算法”“标记-整理算法”。

分代收集理论放到商用Java虚拟机里,设计者至少会把Java堆划分为新生代(Young Generation)和老年代(Old Generation)两个区域
新生代每次垃圾收集时都发现有大批对象死去,而每次回收后存活的少量对象,将会逐步晋升到老年代中存放。

部分收集(Partial GC):指目标不是完整收集整个Java堆的垃圾收集分为:
(1)新生代收集(Minor GC/Young GC):指目标只是新生代的垃圾收集
(2)老年代收集(Major GC/Old GC):指目标只是老年代的垃圾收集
(3)混合收集(Mixed GC):指目标是收集整个新生代及部分老年代的垃圾收集
整堆收集(Full GC):收集整个Java堆和方法区的垃圾收集

4.2. 标记-清除算法

算法分为“标记”和“清除”两个阶段:首先标记出所有需要回收的对象,标记完成后,统一回收掉所有被标记的对象,也可以反过来。
主要缺点有两个:
(1)执行效率不稳定,如果Java堆中包含大量对象,而且其中大部分是需要被回收的,这时必须进行大量标记和清除的动作,导致标记和清除两个过程的执行效率都随对象数量增长而降低
(2)内存空间的碎片化问题,标记、清除之后会产生大量不连续的内存碎片,空间碎片太多可能会导致以后在程序运行过程中需要分配较大对象时无法找到足够的连续内存而不得不提前触发另一次垃圾收集动作。

在这里插入图片描述

4.3. 标记-复制算法

将可用内存按容量划分为大小相等的两块,每次只使用其中的一块,当这一块的内存用完了,就将还存活着的对象复制到另一块上面,然后再把已使用过的内存空间一次清理掉。
缺点:将可用内存缩小为原来的一半,空间浪费太多了。

在这里插入图片描述
优化:
分代回收算法:把新生代分为一块较大的Eden空间和两块较小的Survivor空间,每次分配内存只使用Eden和其中一块Survivor。发生垃圾收集时,将Eden和Survivor中仍然存活的对象一次性复制到另一块Survivor空间上,然后直接清理掉Eden和已用过的那块Survivor空间。HotSpot虚拟机默认Eden和Survivor的大小比例是8:1,即每次新生代中可用内存空间为整个新生代容量的90%(Eden的80%加上一个Survivor的10%),只有一个Survivor空间,即10%的新生代是会被“浪费”的。

4.4. 标记-整理算法

标记-整理算法,其中的标记过程仍然与“标记-清除”算法一样,但不是直接对回收对象进行清理,
而是让所有存活的对象都向内存空间一端移动,然后直接清理掉边界以外的内存。

在这里插入图片描述

5. 经典垃圾收集器

主要介绍G1和CMS这两种收集器

在这里插入图片描述

5.1. Serial收集器

特点:
单线程,进行垃圾收集时,必须暂停其它所有工作线程,直到它收集结束。
优点:
简单而高效,对于内存资源受限的环境,它是所有收集器里额外内存消耗最小的;对于单核处理器或或处理器核心数较少的环境来说,Serial收集器由于没有线程交互的开销,专心做垃圾收集自然可以获得最高的单线程收集效率。

在这里插入图片描述

5.2. ParNew收集器

ParNew收集器实质上是Serial收集器的多线程并行版本,使用多条线程进行垃圾收集,
在服务端模式下的HotSpot虚拟机

在这里插入图片描述

在JDK5中使用CMS来收集老年代的时候,新生代只能选择ParNew或者Serial收集器中的一个。G1是一个面向全堆的收集器

5.3. Parallel Scavenge收集器

基于标记-复制算法,并行收集的多线程收集器,Parallel Scavenge收集器目标是达到一个可控制的吞吐量。

在这里插入图片描述

Parallel Scavenge收集器提供两个参数用于精确控制吞吐量:
最大垃圾收集停顿时间:-XX:MaxGCPauseMilis参数
吞吐量大小:-XX:GCTimeRatio参数

5.4. Serial Old收集器

Serial Old是Serial收集器的老年代版本,它同样是一个单线程收集器,使用标记-整理算法。

在这里插入图片描述

5.5. Parallel Old收集器

Parallel Old 是 Parallel Scavenge收集器的老年代版本,支持多线程并发收集,基于标记-整理算法。

在这里插入图片描述

5.6. CMS收集器

CMS(Concurrent Mark Sweep)收集器是一种以获取最短回收停顿时间为目标的收集器。
CMS收集器基于标记-清除算法,运作过程相对于前面几种收集器更复杂一些,整个过程分为四个步骤:
(1)初始标记
(2)并发标记
(3)重新标记
(4)并发清除
其中初始标记,重新标记这两个步骤仍然需要“Stop The World”

在这里插入图片描述

5.7. Garbage First收集器(G1)

G1是一款主要面向服务端应用的垃圾收集器。G1不在坚持固定大小以及固定数量的分代区域划分,而是把连续的Java堆划分为多个大小相等的独立区域(Region),
每一个Refion都可以根据需要,扮演新生代的Eden空间、Survivor空间,或者老年代空间。
G1收集器的运作过程步骤:
(1)初始标记:仅仅只是标记一下GC Roots能直接关联到的对象
(2)并发标记:从GC Root开始对堆中对象进行可达性分析,递归扫描整个堆里的对象图,找出要回收的对象,这阶段耗时很长,但可与用户程序并发执行。
(3)最终标记:对用户线程做另一个短暂的暂停,用于处理并发阶段结束后仍遗留下来的最后那少量的SATB记录。
(4)筛选回收:负责更新Region的统计数据,对各个Region的回收价值和成本进行排序,根据用户所期望的停顿时间来制定回收计划,可以自由选择任意多个Region构成回收集,然后把决定回收的那一部分Region的存活对象复制到空的Region中,再清理掉整个旧Region的全部空间。这里的操作涉及存活对象的移动,是必须暂停用户线程,由多条收集器线程并行完成的。

在这里插入图片描述

上一篇:Java虚拟机 - 运行时数据区域

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值