JVM垃圾回收机制总结(2) :基本算法概述
1、引用计数收集器 (Reference Counting)
引用计数是垃圾收集的早期策略。在这种方法中,堆中每一个对象都有一个引用计数。一个对象被创建了,并且指向该对象的引用被分配给一个变量,这个对象的引用计数被置为1。当任何其他变量被赋值为对这个对象的引用时,计数加1。当一个对象的引用超过了生存期或者被设置一个新的值时,对象的引用计数减1。任何引用计数为0的对象可以被当作垃圾收集。当一个对象被垃圾收集的时候,它引用的任何对象计数值减1。 在这种方法中,一个对象被垃圾收集后可能导致后续其他对象的垃圾收集行动。
这种方法的好处是,引用计数收集器可以很快地执行,交织在程序的运行之中。这个特性对于程序不能被长时间打断的实时环境很有利。坏处就是,引用计数无法检测出循环(即两个或者更多的对象互相引用)。 循环的例子如,父对象有一个对子对象的引用,子对象又反过来引用父对象。这些对象永远都不可能计数为0,就算它们已经无法被执行程序的根对象可触及。还有一个坏处就是,每次引用计数的增加或者减少都带来额外开销。
因为引用计数方法固有的缺陷,这种技术现在已经不为人所接受。现实生活中所遇到的Java虚拟机更有可能在垃圾收集堆中使用追踪算法。
2、标记-清除收集器(Mark-Sweep)或追踪回收器( Tracing Collector)
跟踪收集器追踪从根结点开始的对象引用图。在追踪过程中遇到的对象以某种方式打上标记。 总的来说,要么在对象本身设置标记,要么用一个独立的位图来设置标记。当追踪结束时,未被标记的对象就知道是无法触及的,从而可以被收集。
基本的追踪算法被称作“标记并清除”。这个名字指出垃圾收集过程的两个阶段。在标记阶段,垃圾收集器遍历引用树,标记每一个遇到的对象。在清除阶段,未被标记的对象被释放了,使用的内存被返回到正在执行的程序。在Java虚拟机中,清除步骤必须包括对象的终结。下面的图示显示了这种收集器的收集过程:
垃圾回收器回收了堆中的对象之后,自然会在堆内存中遗留下一些内存空闲的间隙,下面就是完成上图所示的标记-清除垃圾回收之后的堆内存变化,我们能很清楚的看到这一点。
下面我们就要讨论如何对付这些回收对象后产生的内存碎块的垃圾回收器。
3、基于标记-清除的压缩收集器
压缩方法把活动的对象越过空闲区滑动到堆的一端,在这个过程中,堆的另一端出现一个大的连续空闲区。所有被移动的对象的引用也被更新,指向新的位置。
更新被移动的对象的引用有时候通过一个间接对象引用层可以变得更简单。不直接引用堆中的对象,对象的引用实际上指向一个对象句柄表。对象句柄才指向堆中对象的实际位置。当对象被移动了,只有这个句柄需要被更新为新位置。所有的程序中对这个对象的引用仍然指向这个具有新值的句柄,而句柄本身没有移动。这种方法简化了消除堆碎块的工作,但是每一次对象访问都带来了性能损失。
4、拷贝收集器
一般的拷贝算法被称为“停止并拷贝”、,在这个方案中,堆被分为两个区域,任何时候都只使用其中的一个区域。对象在同一个区域中分配,直到这个区域被耗尽。此时,程序执行被中止、堆被遍历,遍历时遇到的活动对象被拷贝到另外一个区域。当停止和拷贝过程结束时。程序恢复执行。内存将从新的堆区域中分配,直到它也被用尽。那时程序将再次中止。遍历堆、活动对象又被拷贝回原来的区域‘这种方一法带来的代价就是,对于指定大小的堆来说需要两倍大小的内存,因为任何时候都只能使用其中的一半。下图就是一个停止并拷贝的过程:
5、按代收集器(Generational Collector)
简单的停止并拷贝收集器的缺点是每一次收集时,所有的活动对象都必须被拷贝。大部分语言的大多数程序都有以下特点,如果我们全面考虑这些,拷贝算法的这个缺点足可以被改进的。
(1) 大多数程序创建的大部分对象都具有很短的生命期。
(2)大多数程序都创建一些具有非常长生命周期的对象。
简单的拷贝收集器浪费效率的一个上要原因就是、它们每次都把这些生命周期很长的对象来回拷贝,消耗大量时间。
按代收集的收集器通过把对象按照寿命来分组解决这个效率低下的问题,更多地收集那些短暂出现的年幼对象,而非寿命较长的对象。在这种方法里,堆被划分成两个或者更多的子堆,
每一个子堆为一“代”对象服务。最年幼的那一代进行最频繁的垃圾收集。因为大多数对象都是短促出现的,只有很小部分的年幼对象可以在它们经历第一次收集后还存活。如果一个最年幼的对象经历了好几次垃圾收集后仍然存活,那么这个对象就成长为寿命更高的一代:它被转移到另外一个子堆中去。年龄更高的每一代的收集都没有年径的那一代来得频繁。每当对象在它所属的年龄层(代)中变得成熟(逃过了多次垃圾收集)之后,它们就被转移到更高的年龄层中去。
按代进行的收集技术除了可以应用于拷贝算法,也可以应用于标记并清除算法。不管在哪种情况下,把堆按照对象年龄层分解都可以提高最基本的垃级收集算法的性能。
总结
引用计数收集器 基本上已经不被使用了。 标记-清除收集器 是目前寻找垃圾对象的主流策略,但是它对回收后产生的内存碎片却无能为力,因此我们必须采用一些合并空闲内存的算法,这就产生了基于标记-清除的 压缩收集器 和 拷贝收集器 。而基于性能原因,拷贝收集器是比较受欢迎的。但是它有一个比较大的弱点:对于寿命较长的对象会不停的拷贝,这就付出了不必要的代价。因此, 按代收集器 的出现 改进了这一性能。而我们的JDK中的JVM就采用了按代收集策略。
垃圾回收的若干问题
(1) 垃圾回收从哪里开始??
上面说到的“引用计数”法,通过统计控制生成对象和删除对象时的引用数来判断。垃圾回收程序收集计数为0的对象即可。但是这种方法无法解决循环引用。所 以,后来实现的垃圾判断算法中,都是从程序运行的根节点出发,遍历整个对象引用,查找存活的对象。那么在这种方式的实现中,垃圾回收从哪儿开始的呢 ? 即,从哪儿开始查找哪些对象是正在被当前系统使用的。上面分析的堆和栈的区别,其中栈是真正进行程序执行地方,所以要获取哪些对象正在被使用,则需要从 Java栈开始。同时,一个栈是与一个线程对应的,因此,如果有多个线程的话,则必须对这些线程对应的所有的栈进行检查。
同时,除了栈外,还有系统运行时的寄存器等,也是存储程序运行数据的。这样,以栈或寄存器中的引用为起点,我们可以找到堆中的对象,又从这些对象找到对 堆中其他对象的引用,这种引用逐步扩展,最终以null引用或者基本类型结束,这样就形成了一颗以Java栈中引用所对应的对象为根节点的一颗对象树,如 果栈中有多个引用,则最终会形成多颗对象树。在这些对象树上的对象,都是当前系统运行所需要的对象,不能被垃圾回收。而其他剩余对象,则可以视为无法被引 用到的对象,可以被当做垃圾进行回收。
因此,垃圾回收的起点是一些根对象(java栈, 静态变量, 寄存器...) 。而最简单的Java栈就是Java程序执行的main函数。这种回收方式,也是上面提到的“标记-清除”的回收方式。
(2) 如何处理碎片??
由于不同Java对象存活时间是不一定的,因此,在程序运行一段时间以后,如果不进行内存整理,就会出现零散的内存碎片。碎片最直接的问题就是会导致无法 分配大块的内存空间,以及程序运行效率降低。所以,在上面提到的基本垃圾回收算法中,基于标记-清除的压缩收集器和拷贝收集器 ,都可以解决碎片的问题。
(3) 如何解决同时存在的对象创建和对象回收问题??
垃圾回收线程是回收内存的,而程序运行线程则是消耗(或分配)内存的,一个回收内存,一个分配内存 ,从这点看,两者是矛盾的。因此,在现有的垃圾回收方式中,要进行垃圾回收前,一般都需要暂停整个应用(即:暂停内存的分配),然后进行垃圾回收,回收完成后再继续应用。这种实现方式是最直接,而且最有效的解决二者矛盾的方式。
但是这种方式有一个很明显的弊端,就是当堆空间持续增大时,垃圾回收的时间也将会相应的持续增大,对应应用暂停的时间也会相应的增大 。 一些对相应时间要求很高的应用,比如最大暂停时间要求是几百毫秒,那么当堆空间大于几个G时,就很有可能超过这个限制,在这种情况下,垃圾回收将会成为系统运行的一个瓶颈。为解决这种矛盾,有了并发垃圾回收算法 ,使用这种算法,垃圾回收线程与程序运行线程同时运行。在这种方式下,解决了暂停的问题,但是因为需要在新生成对象的同时又要回收对象,算法复杂性会大大增加,系统的处理能力也会相应降低,同时,“碎片”问题将会比较难解决。
JVM垃圾回收机制总结(2) :基本算法概述的更多相关文章
- JVM垃圾回收机制和常用算法
由于疫情的原因,所以目前一直在家远程办公,所以很多时间在刷面试题,发现2019大厂的面试虽然种类很多,但是总结了一下发现主要是这几点:算法和数据结构. JVM.集合.多线程.数据库这几点在面试的时候比 ...
- JVM内存管理和JVM垃圾回收机制
JVM内存管理和JVM垃圾回收机制(1) 这里向大家描述一下JVM学习笔记之JVM内存管理和JVM垃圾回收的概念,JVM内存结构由堆.栈.本地方法栈.方法区等部分组成,另外JVM分别对新生代和旧生代采 ...
- JVM基础系列第8讲:JVM 垃圾回收机制
在第 6 讲中我们说到 Java 虚拟机的内存结构,提到了这部分的规范其实是由<Java 虚拟机规范>指定的,每个 Java 虚拟机可能都有不同的实现.其实涉及到 Java 虚拟机的内存, ...
- JVM内存管理、JVM垃圾回收机制、新生代、老年代以及永久代
内存模型 JVM运行时数据区由程序计数器.堆.虚拟机栈.本地方法栈.方法区部分组成,结构图如下所示. JVM内存结构由程序计数器.堆.栈.本地方法栈.方法区等部分组成,结构图如下所示: 1)程序计数器 ...
- JVM 垃圾回收机制和常见算法和 JVM 的内存结构和内存分配(面试题)
一.JVM 垃圾回收机制和常见算法 Sun 公司只定义了垃圾回收机制规则而不局限于其实现算法,因此不同厂商生产的虚拟机采用的算法也不尽相同.GC(Garbage Collector)在回收对象前首先必 ...
- 真的可惜,四面阿里,结果我被JVM垃圾回收机制与 OOM异常卡住了
前言 为什么需要垃圾回收 首先我们来聊聊为什么会需要垃圾回收,假设我们不进行垃圾回收会造成什么后果,我们举一个简单的例子 我们住在一个房子里面,我们每天都在里面生活,然后垃圾都丢在房子里面,又不打扫, ...
- JVM垃圾回收机制总结:调优方法
转载: JVM垃圾回收机制总结:调优方法 JVM 优化经验总结 JVM 垃圾回收器工作原理及使用实例介绍
- JVM垃圾回收机制概述
JVM垃圾回收机制概述 1.定义 是指JVM用于释放那些不再使用的对象所占用的内存. 2.方式 2.1引用计数(早期) 当引用程序创建引用以及引用超出范围时,JVM必须适当增减引用数.当某个对象的引用 ...
- Java虚拟机学习笔记——JVM垃圾回收机制
Java虚拟机学习笔记——JVM垃圾回收机制 Java垃圾回收基于虚拟机的自动内存管理机制,我们不需要为每一个对象进行释放内存,不容易发生内存泄漏和内存溢出问题. 但是自动内存管理机制不是万能药,我们 ...
- jvm 垃圾回收机制和算法(转)
stop-the-world 在学习Java GC 之前,我们需要记住一个单词:stop-the-world .它会在任何一种GC算法中发生.stop-the-world 意味着JVM因为需要执行GC ...
随机推荐
- js之Function原型
在ECMAScript中,Function(函数)类型实际上是对象.每个函数都是Function类型的实例,而且都与其他引用类型一样具有属性和方法.由于函数是对象,因此函数名实际上也是一个指向函数对象 ...
- “我爱淘”冲刺阶段Scrum站立会议5
完成任务: 由于我们的思路做了些改变,重新设计了界面的布局. 计划任务: 实现数据库的链接,将页面布局完善. 遇到问题: 使用webservice对数据的提取
- 小学生四则运算C/C++编程设计思想
题目: 1.题目避免重复: 2.可定制(数量(打印方式)): 3.可控制下列参数:是否有乘除法.是否有括号.数值范围.加减有无负数. 除法有无余数.是否支持分 ...
- fstab文件
格式: 文件系统 挂载点 格式 挂载文件系统选项 备份选项 自检选项 1.指定挂载的设备或者远程文件系统,比如普通的挂载设备/dev/sda1, 对于nfs格式:<host>:<di ...
- 1565: [NOI2009]植物大战僵尸 - BZOJ
Description Input Output仅包含一个整数,表示可以获得的最大能源收入.注意,你也可以选择不进行任何攻击,这样能源收入为0.Sample Input3 210 020 0-10 0 ...
- gvim编辑文件到github乱码
with below _vimrc settings, code uploaded to GitHub will display with proper encoding set encoding=u ...
- lca 最近公共祖先
http://poj.org/problem?id=1330 #include<cstdio> #include<cstring> #include<algorithm& ...
- A beginner’s introduction to Deep Learning
A beginner’s introduction to Deep Learning I am Samvita from the Business Team of HyperVerge. I join ...
- C#正则表达式大全{转}
只能输入数字:"^[0-9]*$". 只能输入n位的数字:"^\d{n}$". 只能输入至少n位的数字:"^\d{n,}$". 只能输入m~ ...
- 首次发布App,In-App Purchase 无法submit for review 问题的解决方案
原地址:http://blog.csdn.net/blucenong/article/details/7819195 一个IDP首次create app 然后首次create new IAP的时候,我 ...