JVM垃圾回收算法分析与演示【纯理论】
继续接着上一次【https://www.cnblogs.com/webor2006/p/10729649.html】的来学习,上次在结尾处提到了JVM常见的GC算法,如下:

接下来则逐一的对其进行学习,不过还是纯理论,比较枯燥但是必须得过一遍。
标记-清除算法(Mark-Sweep):
- 算法分为“标记”和“清除”两个阶段,首先标记出所有需要回收的对象,然后回收所有需要回收的对象。
 - 缺点:
a、效率问题:标记和清理两个过程效率都不高。
b、空间问题:标记清理之后会产生大量不连续的内存碎片,空间碎片太多可能会导致后续使用中无法找到足够的连续内存而提前触发另一次的垃圾搜集动作。 
下面用图表说明一下整个算法的过程,首先初始内存为:

可以看到从栈中有两个引用指向了堆,而堆中的对象存有互相引用的情况,其中左侧的栈可以称为Root GC,它可达的对象为A和B,如下:

其中标绿的是不能被GC回收的,接着继续:


最终内存的可达情况为:

其中标红的则是不能被Root GC所能引用的,也就是应该是被回收掉的,所以被回收之后整个图就变成了:

所以从上图的整个过程也能看到:
- 效率不高,需要扫描所有对象,堆越大,GC越慢。
 - 存在内存碎片问题。GC次数越多,碎片越严重。
 
复制搜集算法(Copying):
- 将可用内存分为两块,每次只使用其中的一块,当半区内存用完了,仅将不存活的对象复制到另外一块上面,然后就把原来整块内存空间一次性清理掉。
 - 这样使得每次内存回收都是对整个半区的回收,内存分配时也就不用考虑内存碎片等复杂情况,只要移动堆顶指针,按顺序分配内存就可以了,实现简单,运行高效。只是这种算法的代价是将内存缩小为原来的一半,代价高昂。
 - 现在的商业虚拟机中都是用了这一种收集算法来回收新生代【啥叫新生代呢?通常情况下刚new出来的对象都会位于新生代当中,当新生代经历了几轮垃圾回收之后,尚未被回收的对象,这时JVM就会认为这些对象的存活时间比较长,则会将它们晋升到老年代中】。
 - 将内存分为一块较大的eden空间和2块较少的survivor【幸存者】空间,每次使用eden和其中一块survivor,当回收时将eden和survivor还存活的对象一次性拷贝到另外一块survivor空间上,然后清理掉eden和用过的survivor。
 - Oracle Hotspot虚拟机默认eden和survivor的大小比例是8:1,也就是每次只有1%的内存是“浪费”的。
 - 复制收集算法在对象存活率高的时候,效率有所下降。
 - 如果不想浪费50%的空间,就需要有额外的空间进行分配担保用于应付半区内存中所有对象都100%存活的极端情况,所以在老年代一般不能直接选用这种算法。
 
下面则来用图来理解一下它的流程:

其中From-Space则是真正对象存放的空间,而To-Space则是垃圾回收之后接收被复制对象的空间,如上图,由于Stack引用了A和C,则会将A和C拷贝到To-Space空间中,而由于C引用了H、K,所以H也会进到To-Space中,如下:

另外由于H又引用了L,所以L也会进来,如下:

而又引用了B、E、I,则都会进来:

其中D、G是没有被Stack所引用,照理只回收这俩就成了,但是!!此时则会将From-Space全部给清空,如下:

下面再对其进行一些特点描述:
- 只需要扫描存活的对象,效率更高。
 - 不会产生碎片。
 - 需要浪费额外的内存作为复制区。
 - 复制算法非常适合生命周期比较短的对象,因为每次GC总能回收大部分的对象,复制的开销比较小。
 - 根据IBM的专门研究,98%的Java对象只会存活1个GC周期,对这些对象很适合用复制算法。而且不用1:1的划分工作区和复制区的空间。
 
标记-整理算法(Mark-Compact):
它的标记过程跟上面的复制搜索算法是一样的,但后续步骤不是进行直接清理,而是令所有存活的对象一端移动,然后直接清理掉这端边界以外的内存。
如图:

可以看到下面就是进行了回收整理的空间,它的特点是:
- 没有内存碎片
 - 比Mark-Sweep(标记清除算法)耗费更多的时间进行compact(压缩整理)
 
分代收集(Generational Collecting)算法:
- 当前商业虚拟机的垃圾收集都是采用“分代收集”(Generational Collecting)算法,根据对象不同的存活周期将内存划分为几块。
 - 一般是把Java堆分作新生代和老年代,这样就可以根据各个年代的特点采用最适当的收集算法,譬如新生代每次GC都有大批对象死去,只有少量存活,那就选用复制算法只需要付出少量对象的复制成本就可以完成收集。
 - 综合前面几种GC算法的优缺点,针对不同生命周期的对象采用不同的GC算法,比如:
New代表新生代,如之前所说它里面的垃圾回收算法可以采用复制算法,而对于Old老年代的内存则可以通过标记清除算法或者标记整理算法。
 - Hotspot JVM6中共划分为三个代:年轻代(Young Generation)、老年代(Old Generation)、和永久代(Permanent Generation,注意这个是对于JDK8之前而言的,在JDK8之后此代是没有了)。如下图:
在最开始时,对象是处于年轻代中,如下:

然后对象是存在其中的Eden Space和From Space中,这俩的比例是可以调整的,其整个默认比例是8:1:1,而当进行垃圾回收时,则会将Eden Space和From Space留下来的对象都转到To Space上去,此时Eden Space和To Space又可以搭配工作,此时图中的From Space就变为了To Space,而图中的To Space又变成了From Space。而经过了几次回收之后,年轻代的对象就会进入到老年代,如下:

而最终还有一个永久代,它是针对JDK8之前而存在的,如下:

下面再具体来看一下各代的概念:
 - 年轻代(Young Generation)
1、新生成的对象都放在新生代。年轻代用复制算法进行GC(理论上,年轻代对象的生命周期非常短,所以适合复制算法)。
2、年轻代分三个区。一个Eden区,两个Survivor区(可以通过参数设置Survivor个数)。对象在Eden区中生成。当Eden区满时,还存活的对象将被复制到一个Survivor区,当这个Survivor区满时,此区的存活对象将被复制到另外一个Survivor区,当第二个Survivor区也满了的时候,从第一个Survivor区复制过来的并且此时还存活的对象,将被复制到老年代。2个Survivor是完全对称,轮流替换。
3、Eden和2个Survivor的缺省比例是8:1:1,也就是10%的空间会被浪费。可以根据GC log的信息调整大小的比例。 - 老年代(Old Generation)
1、存放了经过一次或多次GC还存活的对象。
2、一般采用Mark-Sweep或者Mark-Compact算法进行GC。
3、有多种垃圾收集器可以选择。每种垃圾收集器可以看作一个GC算法的具体实现。可以根据具体应用的需求选用合适的垃圾收集器(追求吞吐量?追求最短的响应时间?)。 - 永久代【注意:从JDK8开始已经用元空间来替代它了】
1、并不属于堆(Heap)。但是GC也会涉及到这个区域。
2、存放了每个Class的结构信息,包括常量池、字段描述、方法描述。与垃圾收集要收集的Java对象关系不大。 
JVM垃圾回收算法分析与演示【纯理论】的更多相关文章
- JVM垃圾回收重要理论剖析【纯理论】
		
JVM学习到这里,终于到学习最兴奋的地方了---垃圾回收,在学习它之前还得对JVM垃圾回收相关理论知识进行了解,然后再通过实践来加深对理论的理解,下面直接开始了解相关的理论: JVM运行时内存数据区域 ...
 - JVM垃圾回收机制总结:调优方法
		
转载: JVM垃圾回收机制总结:调优方法 JVM 优化经验总结 JVM 垃圾回收器工作原理及使用实例介绍
 - JVM内存管理和JVM垃圾回收机制
		
JVM内存管理和JVM垃圾回收机制(1) 这里向大家描述一下JVM学习笔记之JVM内存管理和JVM垃圾回收的概念,JVM内存结构由堆.栈.本地方法栈.方法区等部分组成,另外JVM分别对新生代和旧生代采 ...
 - JDK分析工具&JVM垃圾回收(转)
		
转自:http://blog.163.com/itjin45@126/blog/static/10510751320144201519454/ 官方手册:http://docs.oracle.com/ ...
 - 老李分享:jvm垃圾回收
		
老李分享:jvm垃圾回收 poptest是国内唯一一家培养测试开发工程师的培训机构,以学员能胜任自动化测试,性能测试,测试工具开发等工作为目标.如果对课程感兴趣,请大家咨询qq:908821478 ...
 - jvm - 垃圾回收
		
jvm - 垃圾回收 注意 : 本系列文章为学习系列,部分内容会取自相关书籍或者网络资源,在文章中间和末尾处会有标注 垃圾回收的意义 它使得java程序员不再时时刻刻的关注内存管理方面的工作. 垃圾回 ...
 - JVM垃圾回收机制概述
		
JVM垃圾回收机制概述 1.定义 是指JVM用于释放那些不再使用的对象所占用的内存. 2.方式 2.1引用计数(早期) 当引用程序创建引用以及引用超出范围时,JVM必须适当增减引用数.当某个对象的引用 ...
 - Java虚拟机学习笔记——JVM垃圾回收机制
		
Java虚拟机学习笔记——JVM垃圾回收机制 Java垃圾回收基于虚拟机的自动内存管理机制,我们不需要为每一个对象进行释放内存,不容易发生内存泄漏和内存溢出问题. 但是自动内存管理机制不是万能药,我们 ...
 - JVM基础系列第8讲:JVM 垃圾回收机制
		
在第 6 讲中我们说到 Java 虚拟机的内存结构,提到了这部分的规范其实是由<Java 虚拟机规范>指定的,每个 Java 虚拟机可能都有不同的实现.其实涉及到 Java 虚拟机的内存, ...
 
随机推荐
- javascript Round Function
			
var rounded = Math.round( number * 10 ) / 10; // round to one digit var rounded = Math.round( number ...
 - 读Secrets of the JavaScript Ninja(一)函数
			
理解JavaScript为什么应该作为函数式 在JavaScript中,函数是程序执行过程中的主要模块单元 函数是第一类对象 通过字面量创建 function ninjaFunction(){} 赋值 ...
 - 如何利用Docker构建基于DevOps的全自动CI
			
来自用户的DevOps实践分享,分享从开发代码到生产环境部署的一条龙操作的实践及经验, 包含工具技术的选型及考量.私有代码库与私有镜像库的应用等. (一)容器服务的Rancher选型 1.为什么说是下 ...
 - 【整理】【docker】【Linux】整理笔记
			
1.Linux安装docker 参考文章:https://blog.csdn.net/yanpenglei/article/details/78944553 Docker 要求系统的内核版本高于 3. ...
 - Ubuntu16.04 安装PHP7 的 imagick 扩展
			
转自:https://blog.csdn.net/qq_16885135/article/details/78130281 1.从 https://pecl.php.net/package/imagi ...
 - 寻找最小矩形边框--OpenCv
			
好久没有写博客了 今天写一下比较常用的寻找矩形边框 ////////////////////////////寻找最矩形边框/////////////////////////////////////// ...
 - php中让数组顺序随机化,打乱顺序等
			
php中有很多排序的函数,sort,rsort,ksort,krsort,asort,arsort,natcasesort,这些函数用来对数组的键或值进行这样,或那样的排序. 可以终究有时候还需要一些 ...
 - day22——从空间角度研究类、类与类之间的关系
			
day22 从空间角度研究类 何处添加对象属性 在类的______init______可以添加 class Human: mind = "有思想的" def __init__(se ...
 - tensorflow-笔记02
			
TensorFlow扩展功能 自动求导.子图的执行.计算图控制流.队列/容器 1.TensorFlow自动求导 在深度学习乃至机器学习中,计算损失函数的梯度是最基本的需求,因此TensorFlow也原 ...
 - Go基础编程实践(八)—— 系统编程
			
捕捉信号 // 运行此程序,控制台将打印"Waiting for signal" // 按Ctrl + C 发送信号以关闭程序,将发生中断 // 随后控制台依次打印"Si ...