JVM GC之垃圾收集算法
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之垃圾收集算法的更多相关文章
- JVM三种垃圾收集算法思想及发展过程
JVM垃圾收集算法的具体实现有很多种,本文只是介绍实现这些垃圾收集算法的三种思想和发展过程.所有的垃圾收集算法的具体实现都是遵循这三种算法思想而实现的. 1.标记-清除算法 标记-清除(Mark-Sw ...
- JVM探秘:垃圾收集算法
本系列笔记主要基于<深入理解Java虚拟机:JVM高级特性与最佳实践 第2版>,是这本书的读书笔记. 垃圾收集算法 垃圾收集算法主要有标记-清除算法.复制算法.标记-整理算法.分代收集算法 ...
- 「给产品经理讲JVM」:垃圾收集算法
纠结的我,给我的JVM系列终于起了第三个名字,害,我真是太难了.从 JVM 到 每日五分钟,玩转 JVM 再到现在的给产品经理讲 JVM ,虽然内容为王,但是标题可以让更多的人看到我的文章,所以,历经 ...
- GC原理---垃圾收集算法
垃圾收集算法 Mark-Sweep(标记-清除算法) 标记清除算法分为两个阶段,标记阶段和清除阶段.标记阶段任务是标记出所有需要回收的对象,清除阶段就是清除被标记对象的空间. 优缺点:实现简单,容易产 ...
- JVM GC之垃圾收集器
简述 如果说收集算法时内存回收的方法论,那么垃圾收集器就是内存回收的具体实现.这里我们讨论的垃圾收集器是基于JKD1.7之后的Hotspot虚拟机,这个虚拟机包含的所有收集器如图: Serial 收集 ...
- GC垃圾收集算法
JVM中的垃圾收集算法实现涉及大量的程序细节,而且各个平台的虚拟机操作内存的方法又各不相同,这里介绍几种垃圾收集算法的思想. 1.标记-清除算法 这是最基础的垃圾收集算法,分为“标记”和“清除”两个阶 ...
- 💕《给产品经理讲JVM》:垃圾收集器
前言 在上篇中,我们把 JVM 中的垃圾收集算法有了一个大概的了解,又是一个阴雨连绵的周末,宅在家里的我们又开始了新一轮的学习: 产品大大:上周末我们说了垃圾收集算法,下面是不是要讲一下这些算法的应用 ...
- JVM之GC算法、垃圾收集算法——标记-清除算法、复制算法、标记-整理算法、分代收集算法
标记-清除算法 此垃圾收集算法分为“标记”和“清除”两个阶段: 首先标记出所有需要回收的对象,在标记完成后统一回收所有被标记对象,它的标记过程前面已经说过——如何判断对象是否存活/死去 死去的对象就会 ...
- JVM垃圾收集算法
JVM垃圾收集 1. 判断对象是否存活 引用计数算法 对象添加一个引用计数器,每个地方引用它,计数器值加+1:当引用失效,计算器值减1:任何时刻计数器为0的对象不可能被使用.引用计数算法实现简单,高效 ...
随机推荐
- IDEA的常见的设置和优化(功能)
转载 原文:https://blog.csdn.net/zeal9s/article/details/83544074
- Dubbo相关的基础
Dubbo是一款高性能轻量级的java RPC框架,它提供了三大核心能力:面向接口的远程方法调用,智能容错和负载均衡,以及服务注册与发现. Dubbo是阿里开源的一个项目,现在已经是Apache的顶级 ...
- php 如何将image图片转化为字符串(GD库操作及imagick两种实现方式)
前两天研究php中的 imagick 扩展的时候,突发奇想实现的一个小功能感觉挺有意思,在这里记录一下: 将一张image图片转化为字符串的形式,先上一张效果图.(运行笔记中的代码需要先安装 php_ ...
- 解决npm ERR!Unexpected end of JSON input while paring near (解析附近时JSON输入意外结束)'...."^2.0.0-rc.0","glob"'等npm install错误
摘要 最近更新了一次node,但是更新后npm的命令总是会报 npm WARN deprecated fsevents@2.0.6: Please update: there are crash fi ...
- 小程序 ----踩坑 ---安卓iOS兼容等
关于小程序一些小功能的代码都在这个GitHub上,感兴趣的可以去看看,https://github.com/huihuijiang/miniProgram目前有:列表左滑删除,拖拽浮标 一.小程序坑1 ...
- python实现数据的压缩与归档
楔子 尽管现代计算机系统的存储能力日益增长,但生成数据的增长是永无休止的. 无损(lossless)压缩算法以压缩或解压缩数据花费的时间来换取存储数据所需要的空间,以弥补存储能力的不足. Python ...
- Linux克隆修改配置文件及IP
Linux下安装基本的开发软件比较费劲,特别是安装mysql的时候,这时候就需要学会克隆及直接备份base虚拟机了,下次直接打开,修改网卡文件信息就完事. 克隆虚拟机eth0网卡出现的问题解决1:修改 ...
- 【Linux常用命令】Linux kill, killall, kill -9,
参考链接 https://blog.csdn.net/zong596568821xp/article/details/77899454 kill + PID kill -9 + PID 加上-9 是 ...
- 我的第一个Node.js项目
Node.js的安装通常有两种方式:自己编译源代码和使用编译好的文件,我这里使用编译好的文件目前我的home目录下有刚下载来的node-v4.2.3-linux-x641.首先解压缩 tar xvf ...
- ECMAScript 6 入门——ES6 声明变量的六种方法
ES6 声明变量的六种方法 ES5 只有两种声明变量的方法:var命令和function命令.ES6 除了添加let和const命令,后面章节还会提到,另外两种声明变量的方法:import命令和cla ...