• 极客专栏正式上线!欢迎访问 https://www.jikewenku.com/topic.html
  • 极客专栏正式上线!欢迎访问 https://www.jikewenku.com/topic.html

关于Java虚拟机的GC算法、垃圾收集器,你能说出几种?

技术杂谈 勤劳的小蚂蚁 2个月前 (02-09) 61次浏览 已收录 0个评论 扫描二维码


Java技术体系中所提倡的自动内存管理最终可以归结为自动化地解决了两个问题:给对象分配内存以及回收分配给对象的内存,而且这两个问题针对的内存区域就是Java内存模型中的堆区。

关于对象分配内存问题,如何划分可用空间及其涉及到的线程安全问题,本文将结合垃圾回收策略进一步给出内存分配规则。

垃圾回收机制的引入可以有效的防止内存泄露、保证内存的有效使用,也大大解放了Java程序员的双手,使得他们在编写程序的时候不再需要考虑内存管理。

那么,今天我们就深入讲解关于Java虚拟机的知识,GC都有哪几种算法,以及JVM都有那些垃圾回收器,它们的工作原理。

GC概述

垃圾收集 Garbage Collection 通常被称为“GC”,它诞生于1960年 MIT 的 Lisp 语言,经过半个多世纪,目前已经十分成熟了。

jvm 中,程序计数器、虚拟机栈、本地方法栈都是随线程而生随线程而灭,栈帧随着方法的进入和退出做入栈和出栈操作,实现了自动的内存清理,因此,我们的内存垃圾回收主要集中于 java 堆和方法区中,在程序运行期间,这部分内存的分配和使用都是动态的.


为什么要进行垃圾回收?


我们都知道java虚拟机的内存模型一共包括三个部分:堆 ( Java代码可及的 Java堆 和 JVM自身使用的方法区)、栈 ( 服务Java方法的虚拟机栈 和 服务Native方法的本地方法栈 ) 和 保证程序在多线程环境下能够连续执行的程序计数器。

特别地,我们当时就提到Java堆是进行垃圾回收的主要区域,故其也被称为GC堆;而方法区也有一个不太严谨的表述,就是永久代。总的来说,堆 (包括Java堆 和 方法区)是 垃圾回收的主要对象,特别是Java堆。

关于对象分配内存问题,笔者的博文《JVM 内存模型概述》已经阐述了如何划分可用空间及其涉及到的线程安全问题,本文将结合垃圾回收策略进一步给出内存分配规则。

另外,我们知道垃圾回收机制是Java语言一个显著的特点,其可以有效的防止内存泄露、保证内存的有效使用,从而使得Java程序员在编写程序的时候不再需要考虑内存管理问题。



Java 垃圾回收机制要考虑的问题很复杂,本文阐述了其三个核心问题,包括:

那些内存需要回收?(对象是否可以被回收的两种经典算法: 引用计数法 和 可达性分析算法)

什么时候回收?(堆的新生代、老年代、永久代的垃圾回收时机,MinorGC 和 FullGC)

如何回收?(三种经典垃圾回收算法(标记清除算法、复制算法、标记整理算法)及分代收集算法 和 七种垃圾收集器)

如何确定一个对象是否可以被回收?


判断对象是否存活一般有两种方式:

引用计数:每个对象有一个引用计数属性,新增一个引用时计数加1,引用释放时计数减1,计数为0时可以回收。此方法简单,无法解决对象相互循环引用的问题。

可达性分析(Reachability Analysis):从GC Roots开始向下搜索,搜索所走过的路径称为引用链。当一个对象到GC Roots没有任何引用链相连时,则证明此对象是不可用的。不可达对象。

在Java语言中,GC Roots包括:

 虚拟机栈中引用的对象。

 方法区中类静态属性实体引用的对象。

 方法区中常量引用的对象。

 本地方法栈中JNI引用的对象。

垃圾收集法


01

标记-清除算法


这个应该很容易理解了,标记需要清除的对象,然后直接清理掉,这是最基础的算法了,后面的收集法也都是基于这种思路的

但是它有它的缺点:标记跟清理的效率都很低,标记清理使得内存不规整,如果出现需要分配大内存的情况下,连续的内存不足,那就要触发垃圾收集器回收,而过多的垃圾收集,造成资源浪费,耗费时间,次数多了,影响程序运行流畅。


02

复制算法


由于前一种内存碎片化的问题,所以带来了第二种方法,复制发,依然很容易理解,将内存分成相同的两块,先集中在一块内存上分配。

当垃圾收集器回收以后,将存活的对象复制到另一块内存上保存,这样可以保证内存的有序性,实现简单,运行高效。

但是这种方法,需要将可用内存大小牺牲一半,内存的容量减小,也会增加垃圾收集器工作次数。


03

标记-整理


复制收集算法在对象存活率较高时就要进行较多的复制操作,效率将会变低。 更关键的是,如果不想浪费50%的空间,就需要有额外的空间进行分配担保,以应对被使用的内存中所有对象都100%存活的极端情况,老年代对象存活的比较多,采用复制算法,效率不好。

针对存活内存块上存活对象多的情况下,提出了标记-整理法,标记过程仍然与“标记-清除”算法一样,但后续步骤不是直接对可回收对象进行清理,而是让所有存活的对象都向一端移动,然后直接清理掉端边界以外的内存。


04

分代回收


当前商业虚拟机的垃圾收集都采用“分代收集”(Generational Collection)算法,这种算法并没有什么新的思想,只是根据对象存活周期的不同将内存划分为几块。 

一般是把Java堆分为新生代和老年代,这样就可以根据各个年代的特点采用最适当的收集算法。 

在新生代中,每次垃圾收集时都发现有大批对象死去,只有少量存活,那就选用复制算法,只需要付出少量存活对象的复制成本就可以完成收集。

 而老年代中因为对象存活率高、 没有额外空间对它进行分配担保,就必须使用“标记—清理”或者“标记—整理”算法来进行回收。

垃圾收集器


如果说垃圾收集算法是内存回收的方法论,那么垃圾收集器就是内存回收的具体实现。

下图展示了7种作用于不同分代的收集器,其中用于回收新生代的收集器包括Serial、PraNew、Parallel Scavenge,回收老年代的收集器包括Serial Old、Parallel Old、CMS

还有用于回收整个Java堆的G1收集器。不同收集器之间的连线表示它们可以搭配使用。



Serial收集器(复制算法): 新生代单线程收集器,标记和清理都是单线程,优点是简单高效;

Serial Old收集器 (标记-整理算法): 老年代单线程收集器,Serial收集器的老年代版本;

ParNew收集器 (复制算法): 新生代收并行集器,实际上是Serial收集器的多线程版本,在多核CPU环境下有着比Serial更好的表现;

Parallel Scavenge收集器 (复制算法): 新生代并行收集器,追求高吞吐量,高效利用 CPU。吞吐量 = 用户线程时间/(用户线程时间+GC线程时间),高吞吐量可以高效率的利用CPU时间,尽快完成程序的运算任务,适合后台应用等对交互相应要求不高的场景;

Parallel Old收集器 (标记-整理算法): 老年代并行收集器,吞吐量优先,Parallel Scavenge收集器的老年代版本;

CMS(Concurrent Mark Sweep)收集器(标记-清除算法): 老年代并行收集器,以获取最短回收停顿时间为目标的收集器,具有高并发、低停顿的特点,追求最短GC回收停顿时间。

G1(Garbage First)收集器 (标记-整理算法): Java堆并行收集器,G1收集器是JDK1.7提供的一个新收集器,G1收集器基于“标记-整理”算法实现,也就是说不会产生内存碎片。

此外,G1收集器不同于之前的收集器的一个重要特点是:G1回收的范围是整个Java堆(包括新生代,老年代),而前六种收集器回收的范围仅限于新生代或老年代。

丨极客文库, 版权所有丨如未注明 , 均为原创丨
本网站采用知识共享署名-非商业性使用-相同方式共享 3.0 中国大陆许可协议进行授权
转载请注明原文链接:关于Java虚拟机的GC算法、垃圾收集器,你能说出几种?
喜欢 (0)
[247507792@qq.com]
分享 (0)
勤劳的小蚂蚁
关于作者:
温馨提示:本文来源于网络,转载文章皆标明了出处,如果您发现侵权文章,请及时向站长反馈删除。

您必须 登录 才能发表评论!

  • 精品技术教程
  • 编程资源分享
  • 问答交流社区
  • 极客文库知识库

客服QQ


QQ:2248886839


工作时间:09:00-23:00