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的对象不可能被使用.引用计数算法实现简单,高效 ...
随机推荐
- CSS 实现居中 + 清除浮动
一.水平居中 1.行内元素:text-align:center; 2.块级元素:margin:0 auto; 3.绝对定位和移动:absolute + transform 4.绝对定位和负边距:abs ...
- 未找到或无法访问服务器。请验证实例名称是否正确并且 SQL Server 已配置为允许远程连接。 (provider: Named Pipes Provider, error: 40 - 无法打开到 SQL Server 的连接) (.Net SqlClient Data Provider)
今天连接服务器的SQL Server 遇到了一个很经典的问题 之前也曾多次遇到过 这次记录一下 按照之前经验 首先 开启了服务中的 SQL Server(MSSQLSERVER)和ASP.NET St ...
- wordpress程序打开太慢的解决方案(一步搞定)
周末两天没有打开自己的赵一鸣随笔博客,今天访问了一下,打开速度太慢了,看看浏览器栏目,网站标题已经显示出来了,但是网页却是一片空白,什么都没有,刚开始以为是我们公司网速的问题,就没有特别注意这件事情. ...
- Oracle cmd 命令
1.登陆 输入sqlplus,回车.然后输入用户名和密码. 退出exit. 2.查看服务 Window打开服务的cmd命令 windows +R :services.msc---本地服务设置 rege ...
- WPF实战案例-在线程内同步集合数据到UI线程
有这样一个场景,在vm中,我们为了ui的体验,会异步访问后端接口,获取数据集合,如果这个集合绑定到界面,并且在线程内,怎么处理? 有人讲:this.Dispatcher.Invoke,如果在vm内呢? ...
- JavaSpring【六、AOP的API】
AOP API Spring1.2历史用法,现在仍然支持 现在xml配置和注解的用法是基于API的,只是比较简便
- 第一章、Django概述
目录 第一章.Django概述 一.了解软件开发架构 二.HTTP协议 三.响应状态码 四.请求方式 五.基于wsgiref模块 六..动静态网页 七.python三大主流web框架 八.安装Djan ...
- 玩转springcloud(一):什么是Springcloud ,有什么优缺点? 学习顺序是什么?
一.首先看官方解释: Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智能路由,微代理,控制总线).分布式系统的协调导致了样板模式, 使 ...
- RT-Thread代码启动过程与$Sub$ $main、$Super$ $main
文章转载自:https://blog.csdn.net/yang1111111112/article/details/80913001 我们找到系统复位的地方,可以往下单步跟踪. ①从系统初始化开始执 ...
- vim文本编辑及文件查找应用2
vim编辑器: vim末行模式: 内建的命令行接口 (1)地址定界 :start_pos[,end_pos] #:特定的第#行,例如5即第5行 .:当前行 .,+#:当前行,加#行的行范围 $:最后一 ...