1.垃圾收集概念

GC目的

  • 分配内存,为每个新建的对象分配空间
  • 确保还在使用的对象的内存一直还在,不能把有用的空间当垃圾回收了
  • 释放不再使用的对象所占用的空间

我们把还被引用的对象称为活的,把不再被引用的对象认为是死的,也就是我们说的垃圾。GC 的工作就是找到死的对象,释放(也称为回收)这些对象所使用的空间的过程称为垃圾收集。

我们把 GC 管理的内存称为 堆(heap),垃圾收集启动的时机取决于各个垃圾收集器,通常,垃圾收集发生于整个堆或堆的部分已经被使用光了,或者使用的空间达到了某个百分比阈值

对于内存分配,实现的难点在于在堆中找到一块没有被使用的确定大小的内存空间。所以,对于大部分垃圾回收算法来说避免内存碎片化是非常重要的,它将使得空间分配更加高效。

GC收集器理想状态

安全和全面

活的对象一定不能被清理掉,死的对象一定不能在几个回收周期结束后还在内存中。

高效

不能将我们的应用程序挂起太长时间。我们需要在时间、空间、频次上作出权衡。比如,如果堆内存很小,每次垃圾收集就会很快,但是频次会增加。如果堆内存很大,很久才会被填满,但是每一次回收需要的时间很长。

内存碎片限制

当对垃圾对象的内存被释放时,空闲空间可能会出现在不同区域的小块中,这样在任何一个相邻区域中都可能没有足够的空间用于分配一个大型对象。消除分段的一种方法称为压缩,在下面的各种垃圾收集器设计选择中将讨论。

可伸缩性

可伸缩性也很重要。在多处理器系统中,分配不应该成为多线程应用程序的可伸缩性瓶颈,而且收集也不应该成为瓶颈。

2.GC算法选择

在设计或选择垃圾收集算法时,必须做出一些选择:

串行 vs 并行

并行:多个垃圾回收线程同时工作,互不影响

并发:垃圾回收线程和应用程序线程同时工作,应用程序不需要挂起

串行收集的情况,即使是多核 CPU,也只有一个核心参与收集。使用并行收集器的话,垃圾收集的工作将分配给多个线程在不同的 CPU 上同时进行。并行可以让收集工作更快,缺点是带来的复杂性和内存碎片问题。

并发 vs Stop-the-world

当 stop-the-world 垃圾收集器工作的时候,应用将完全被挂起。与之相对的,并发收集器在大部分工作中都是并发进行的,也许会有少量的 stop-the-world。

stop-the-world 垃圾收集器比并发收集器简单很多,因为应用挂起后堆空间不再发生变化,它的缺点是在某些场景下挂起的时间我们是不能接受的(如 web 应用)。

相应的,并发收集器能够降低挂起时间,但是也更加复杂,因为在收集的过程中,也会有新的垃圾产生,同时,需要有额外的空间用于在垃圾收集过程中应用程序的继续使用。

压缩 vs 不压缩 vs 复制

垃圾回收器确定了内存中哪些对象是活的,哪些是垃圾,它可以压缩内存,将所有的活动对象一起移动,并完全回收剩余的内存。在压缩之后,在第一个空闲位置分配一个新对象是很容易和快速的。可以使用一个简单的指针来跟踪对象分配的下一个位置。

与压缩收集器相反,不压缩的收集器只会就地释放空间,不会移动存活对象。优点就是快速完成垃圾收集,缺点就是潜在的碎片问题。一般来说,从堆中进行分配比从压缩堆中分配更昂贵。可能需要在堆中搜索足够大的连续内存区域以容纳新对象。

第三种选择是复制收集器,它将活动对象复制到另一个内存区域。这样做的好处是,原有区域的空间被清空了,这样后续分配对象空间非常迅速,缺点就是需要进行复制操作和占用额外的空间。

3.性能指标

以下几个是评估垃圾收集器性能的一些指标:

  • 吞吐量:应用程序的执行时间占总时间的百分比,当然是越高越好
  • 垃圾收集开销:垃圾收集时间占总时间的百分比
  • 停顿时间:垃圾收集过程中导致的应用程序挂起时间
  • 频次:相对于应用程序来说,垃圾收集的频次
  • 空间:垃圾收集占用的内存
  • 及时性:当对象变为垃圾和内存可用时之间的时间

在交互式程序中,通常希望是低延时的,而对于非交互式程序,总运行时间比较重要。实时应用程序既要求每次停顿时间足够短,也要求总的花费在收集的时间足够短。在小型个人计算机和嵌入式系统中,则希望占用更小的空间。

4.垃圾收集算法

4.1.标记-清除算法

概念

最基础的垃圾收集算法就是“标记-清除算法”,如同它的名字,该算法分为“标记”和“清除”两个阶段。之所以是最基础算法是因为后续的几种收集算法都是基于这种思路并对其不足进行改进而得到的。

不足

第一,效率问题,标记和清除两个阶段的效率都不高

第二,空间问题,标记清除之后会产生大量不连续的内存碎片,空间碎片太多会导致需要分配较大对象时,无法找到足够的连续内存而不得不提前出发另一次垃圾收集动作。

标记-清除算法示意图

4.2.复制算法

概念

为了解决效率问题,复制算法便出现了,它将可用的内存按照容量等分成两块,每次只使用其中的一块,当这一块的内存用完了,就将存活的对象复制到另外一块内存,然后将原有内存块的空间清理掉。这样每次只对整个半区内存进行垃圾回收,内存分配时就无需考虑内存碎片等复杂的情况了,只需要移动堆顶的指针,按顺序分配内存即可,实现简单,运行效率高。

不足

将内存大小一分为二,只使用其中一份,代价太高

复制算法示意图

使用场景

新生代正是采用复制算法进行垃圾收集,将新生代内存分为一块较大的Eden空间和两块较小的Survivor空间,每次使用Eden和其中一个Survivor。当回收时,将存活的对象复制到另外一块Survivor空间上,最后清理掉刚才使用过的Eden和Survivor空间。

HotSpot虚拟机默认Eden和Survivor的大小比例是,8:1:1,也就是新生代中可用内存空间为整个新生代容量的90%(80%+10%),只有10%的内存会被“浪费”。但是当Survivor空间不够用时,需要依赖其它内存(老年代)进行分配担保(Handle Promotion)。

如果另外一块Survivor空间没有足够空间存放新生代收集下来的存活对象时,这些对象将直接通过分配担保机制进入老年代。

4.3.标记-整理算法

概念

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

根据老年代的特点,“标记-整理算法(Mark-Compact)”就应运而生了,标记过程与“标记-清除算法”一样,但后续不是直接对可回收对象进行清理,而是让所有存活的对象都像一端移动,然后清理掉边界以外的内存。

标记-整理算法示意图

使用场景:老年代

4.4.分代收集算法

当使用分代收集算法时,内存将被分为不同的代(generation),最常见的就是分为年轻代和老年代。



在不同的分代中,可以根据不同的特点使用不同的算法:

- 在新生代,每次垃圾收集时会发现有大批对象死去,只有少量存活,那就选择“复制算法”

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

年轻代中的收集是非常频繁的、高效的、快速的,因为年轻代空间中,通常都是小对象,同时有非常多的不再被引用的对象。

那些经历过多次年轻代垃圾收集还存活的对象会晋升到老年代中,老年代的空间更大,而且占用空间增长比较慢。这样,老年代的垃圾收集是不频繁的,但是进行一次垃圾收集需要的时间更长。

对于新生代,需要选择速度比较快的垃圾回收算法,因为新生代的垃圾回收是频繁的。

对于老年代,需要考虑的是空间,因为老年代占用了大部分堆内存,而且针对该部分的垃圾回收算法,需要考虑到这个区域的垃圾密度比较低。

参考资料:

《深入理解Java虚拟机:Java高级特性与最佳实践》

《Java内存管理白皮书》:http://www.oracle.com/technetwork/java/javase/memorymanagement-whitepaper-150215.pdf

JVM GC之垃圾收集算法的更多相关文章

  1. JVM三种垃圾收集算法思想及发展过程

    JVM垃圾收集算法的具体实现有很多种,本文只是介绍实现这些垃圾收集算法的三种思想和发展过程.所有的垃圾收集算法的具体实现都是遵循这三种算法思想而实现的. 1.标记-清除算法 标记-清除(Mark-Sw ...

  2. JVM探秘:垃圾收集算法

    本系列笔记主要基于<深入理解Java虚拟机:JVM高级特性与最佳实践 第2版>,是这本书的读书笔记. 垃圾收集算法 垃圾收集算法主要有标记-清除算法.复制算法.标记-整理算法.分代收集算法 ...

  3. 「给产品经理讲JVM」:垃圾收集算法

    纠结的我,给我的JVM系列终于起了第三个名字,害,我真是太难了.从 JVM 到 每日五分钟,玩转 JVM 再到现在的给产品经理讲 JVM ,虽然内容为王,但是标题可以让更多的人看到我的文章,所以,历经 ...

  4. GC原理---垃圾收集算法

    垃圾收集算法 Mark-Sweep(标记-清除算法) 标记清除算法分为两个阶段,标记阶段和清除阶段.标记阶段任务是标记出所有需要回收的对象,清除阶段就是清除被标记对象的空间. 优缺点:实现简单,容易产 ...

  5. JVM GC之垃圾收集器

    简述 如果说收集算法时内存回收的方法论,那么垃圾收集器就是内存回收的具体实现.这里我们讨论的垃圾收集器是基于JKD1.7之后的Hotspot虚拟机,这个虚拟机包含的所有收集器如图: Serial 收集 ...

  6. GC垃圾收集算法

    JVM中的垃圾收集算法实现涉及大量的程序细节,而且各个平台的虚拟机操作内存的方法又各不相同,这里介绍几种垃圾收集算法的思想. 1.标记-清除算法 这是最基础的垃圾收集算法,分为“标记”和“清除”两个阶 ...

  7. 💕《给产品经理讲JVM》:垃圾收集器

    前言 在上篇中,我们把 JVM 中的垃圾收集算法有了一个大概的了解,又是一个阴雨连绵的周末,宅在家里的我们又开始了新一轮的学习: 产品大大:上周末我们说了垃圾收集算法,下面是不是要讲一下这些算法的应用 ...

  8. JVM之GC算法、垃圾收集算法——标记-清除算法、复制算法、标记-整理算法、分代收集算法

    标记-清除算法 此垃圾收集算法分为“标记”和“清除”两个阶段: 首先标记出所有需要回收的对象,在标记完成后统一回收所有被标记对象,它的标记过程前面已经说过——如何判断对象是否存活/死去 死去的对象就会 ...

  9. JVM垃圾收集算法

    JVM垃圾收集 1. 判断对象是否存活 引用计数算法 对象添加一个引用计数器,每个地方引用它,计数器值加+1:当引用失效,计算器值减1:任何时刻计数器为0的对象不可能被使用.引用计数算法实现简单,高效 ...

随机推荐

  1. Iedis - Redis 在IDEA中的可视化工具破解

    2.如何破解 // 如果你没有改动IDEA的话,IDEA的插件库在这个目录下C:\Users\Administrator\.IntelliJIdea2017.3\config\plugins\Iedi ...

  2. hdu 1869 枚举+Dijstra

    一点小变形就是了..] #include<iostream> #include<cstdio> #define maxn 201 #define inf 999999 usin ...

  3. .Net下二进制形式的文件存储与读取

    .Net下图片的常见存储与读取凡是有以下几种:存储图片:以二进制的形式存储图片时,要把数据库中的字段设置为Image数据类型(SQL Server),存储的数据是Byte[].1.参数是图片路径:返回 ...

  4. django+mysql(1)

    报错误:mysqlclient 1.3.13 or newer is required; you have 0.9.3 第一种: django降到2.1.4版本就OK了 第二种(仍使用django 2 ...

  5. python 读取文件行

    将文件转化成二进制码,并读取行数,计算总行数 import os Str=input("请输入路径") Sum=0 def read(Str): a = os.listdir(St ...

  6. Vue-img-preload

    预加载页面上的图片资源,提高用户体验 效果预览 使用方法 下载vue-img-preload插件 npm install vue-img-preload 配置参数 eachLoaded(functio ...

  7. python+requests模拟登陆 学校选课系统

    最近学校让我们选课,每天都有不同的课需要选....然后突发奇想试试用python爬学校选课系统的课程信息 先把自己的浏览器缓存清空,然后在登陆界面按f12 如图: 可以看到登陆时候是需要验证码的,验证 ...

  8. Python3 GUI:PyQt5环境搭建

    配置镜像源 最近用Python内置的Thinter写了个小工具,发现界面略朴素,于是决定转向PyQt5.先配置镜像源,否则只能龟速下载. C:\Users\你的用户名下新建目录pip 在pip目录下新 ...

  9. dedeampz 套件关于PHP开启curl方法

    php开启curl方法主要用到三个文件libeay32.dll,php_curl.dll,ssleay32.dll 打开dede的安装目录,更改对应版本PHP中的php.ini文件,在 ; exten ...

  10. 【异常】Cannot run program "git" (in directory "/mnt/software/azkaban-3.79.0"): error=2, No such file or directory

    1 安装azkaban异常 cloudera-scm@cdh4 azkaban-3.79.0]$ ./gradlew build -x test Parallel execution with con ...