Java虚拟机垃圾收集器
一、判断对象存活的算法
1、引用计数(Reference Counting)算法
给对象添加一个引用计数器,每当有一个地方引用时,计数器加1。当引用失效时,计数器减1。当计数器的值为0的时候说该对象不可能再被使用。引用计数器算法的实现简单,效率高,比如微软的COM(Component Object Model)技术,Python等。但是Java虚拟机没有使用该技术,因为该技术难以解决对象循环依赖的问题,即A中有B,B中有A这种情况。
2、可达性分析(Reachability Anlysis)算法
通过一系列称为"GC Roots"的对象作为起始点,从这些节点开始向下搜索,搜索所走过的路径称为引用链(Reference Chain),当一个对象到"GC Roots"没有任何引用链相连是,说明该对象不可用了,即该对象被判定为可回收对象。java中可作为GC Roots的对象:
1) 虚拟机栈(栈帧中的本地变量表)中引用的对象。
2) 方法区中类静态属性引用的对象。
3) 方法区中常量引用的对象。
4) 本地方法栈中JNI(一般指Native方法)引用的对象。
3、Java的几种引用
主要指4种引用:强引用(Strong Reference)、软引用(Soft Reference)、弱引用(Weak Reference)、虚引用(Phantom Reference),这四种引用强度依次逐渐减弱。
1)强引用:代码中普遍存在,如Object obj = new Object()这类引用,只要引用还存在,垃圾收集器永远不会回收掉被引用的对象。
2)软引用:描述一些有用但并非必须的对象。软引用关联的对象,在系统内存溢出之前,将会把这些对象列入回收范围进行第二次垃圾回收。如果这次回收还没有足够的内存,则抛出内存溢出异常。使用SoftReference类来实现。
3)弱引用:用来描述非必须的对象。弱引用关联的对象,只能生存到下一次垃圾回收之前。下一次垃圾回收的时候,无论当前内存是否足够,都会回收掉被弱引用关联的对象。使用WeakReference类来实现。
4)虚引用:一个对象是否有虚引用存在,完全不会对其生存时间造成影响。也无法通过一个虚引用来取的对象实例。为一个对象设置虚引用关联,唯一的目的就是在这个对象在被收集器回收时收到一个系通知。使用PhantomReference类来实现。
4、Java对象的生存或者死亡
在可达性分析中不可达的对象也并非"非死不可",要真正判断一个对象是否死亡,至少要经过两个阶段:第一个阶段是对象没有和GC Roots相连的引用链。那该对象会被第一次标记并进行第一次筛选,筛选条件为该对象有没有必要执行finalize()方法。当没有覆盖finalize()方法,或者finalize()方法已经被调用过,则判定为没有必要执行。
如果判定有必要执行finalize()方法,则虚拟机会将其放到一个F-Queue的队列中,并在稍后创建一个低优先级的Finalizer去执行它。这里的"执行"指的是触发这个方法,但不承诺等待该方法运行结束。这是为了防止线程一直等待,可能导致虚拟机奔溃。finalize()方法是对象逃脱死亡的最后一次机会,稍后GC会对F-Queue中的对象进行第二次小规模标记。如果对象在此时成功的拯救自己,即把自己和GC Roots引用链连接上。则第二次标记时它将被移出"即将回收集合",如果对象这时候还没有逃脱,基本上就真的被回收了。注:任何对象的finalize()方法都只会被系统调用一次。
5、回收方法区
Java虚拟机规范说过可以不需要对方法区进行垃圾回收。在方法区中进行垃圾回收性价比比较低,在新生代中一次垃圾回收可以回收70%~95%的空间,而方法区的效率就远低于此了。永久代的回收主要分为两方面:废弃常量(JDK 1.7之前)和无用类。回收废弃常量和堆中的类似,只要没有和GC roots相连则可回收。而判定一个类是否是无用类的条件就比较苛刻了。需要满足三点:
1)该类所有实例都已被回收,即Java堆中不存在该类的任何实例。
2)加载该类的ClassLoader已经被回收。
3)该类对应的java.lang.Class类没有在任何地方被引用,无法在任何地方通过反射再访问该类的方法。
是否对类进行回收,HotSpot虚拟机提供了-Xnoclassgc参数进行控制,还可以使用-verbose:class和-XX:+TraceClassLoading、-XX:TraceClassUnLoading查看类的加载和卸载信息。其中-verbose:class和-XX:+TraceClassLoading可以在Product版中的虚拟机中使用,-XX:TraceClassUnLoading需要在FastDebug版本的虚拟机才支持。
在大量使用动态代理、反射、CGLib等大量使用ByteCode的框架,动态生成JSP以及OSGI这个频繁自定义ClassLoader的场景都需要虚拟机具备类卸载的功能,以保证永久代不会溢出。
二、垃圾收集算法:
1、标记-清除(Mark_Sweep)
该算法分为"标记"和"清除"两个阶段:首先标记出所有需要回收的对象,在标记完成后统一回收所有被标记对象。该算法是最基础的算法,之所以说是最基础的算法是因为后续的算法都是基于该算法的不足之处进行改进得到的。这个算法有两个不足点:
1) 效率问题,标记和清除两个阶段效率都不高。
2) 空间问题,标记清除后会产生大量不连续的内存碎片,内存碎片太多可能会导致以后程序运行中,需要分配大对象时无法找到足够的连续内存空间,从而不得不提前触发另一次垃圾回收。

2、复制算法(Copying)
为了解决效率问题该算法出现了,它将可用内存按容量划分为等量的两块,每次只使用一块。当这块内存使用完了之后,就将存货的对象复制到另一块上,然后把使用过的这块内存一次清理掉。这样使得每次回收都是对半块内存进行回收,就不用考虑内存碎片的问题了。只要移动堆顶的指针,顺序分配内存就可以了,实现简单,运行高效。但是缺点就是造成了空间的浪费,一半的闲置内存代价太高。
现在的商业虚拟机都采用这种方式来回收新生代,经研究表明新生代的对象98%都是"朝生夕死",所以并不需要按照1:1的比例来划分。而是将内存划分为一块较大的Eden区和两块较小的Survivor区,每次只是用Eden区和一块Survivor区,当进行垃圾回收的时候,将Eden区和正在是用的Survivor区中存活的对象一次性拷贝到另一块Survivor区中,最后再清理掉Eden区和已使用过的Survivor区。HotSpot默认的Eden区和Survivor区的比例是8:1,即每次新生代中可用内存为(80%+10%),也就是说只有10%会被"浪费"。当然98%的对象可回收是一般场景下的数据,我们没法保证每次都只有不多于10%的对象存活,当Survivor不够时,我们需要依靠别的内存(这里指的是老年代)来担保(Handle Promotion)。当另一块Survivor没法存放上一次回收时的存活对象时,这些对象会直接通过分配担保进入老年代。

3、 标记-整理(Mark-Compact)
复制算法在对象存活率较高的情况下要进行较多的复制,效率比较低下。如果不想浪费50%的空间,就需要额外的空间进行分配担保,以应对被使用的内存中所有对象都100%存货的极端情况,所以老年代都不使用这种算法。根据老年代的特征,有人提出了"标记-整理"算法,该算法和"标记-清除"算法一样,但后续步骤不是直接对可回收对象进行清理,而是先将所有存活对象移动到一端,然后清理掉端边境以外的内存区域。

4、 分代收集算法(Generational Collection)
当前商业虚拟机都是采用的这种算法,这种算法只是根据对象的存活周期将内存划分为多块。一般是把虚拟机划分为新生代和老年代,这样就可以根据各个年代的特点采用合适的手机算法了。在新生代中,每次回收都会有大量的对象死去,少量存活,这样就可以采用复制算法了,只需要付出少量对象复制的代价就可以完成垃圾回收了。而在老年代中对象存活率高、没有额外的空间对它进行分配担保,即必须使用"标记-清除"或"标记-整理"算法了。
Java虚拟机垃圾收集器的更多相关文章
- Java虚拟机垃圾收集器与内存分配策略
Java虚拟机垃圾收集器与内存分配策略 概述 那些内存须要回收,什么时候回收.怎样回收是GC须要完毕的3件事情. 程序计数器.虚拟机栈与本地方法栈这三个区域都是线程私有的,内存的分配与回收都具有确定性 ...
- 深入理解java虚拟机【Java虚拟机垃圾收集器】
Java堆内存被划分为新生代和年老代两部分,新生代主要使用复制和标记-清除垃圾回收算法,年老代主要使用标记-整理垃圾回收算法,因此java虚拟中针对新生代和年老代分别提供了多种不同的垃圾收集器,JDK ...
- (转)《深入理解java虚拟机》学习笔记4——Java虚拟机垃圾收集器
Java堆内存被划分为新生代和年老代两部分,新生代主要使用复制和标记-清除垃圾回收算法,年老代主要使用标记-整理垃圾回收算法,因此java虚拟中针对新生代和年老代分别提供了多种不同的垃圾收集器,JDK ...
- 深入理解java虚拟机----->垃圾收集器与内存分配策略(下)
1. 前言 内存分配与回收策略 JVM堆的结构分析(新生代.老年代.永久代) 对象优先在Eden分配 大对象直接进入老年代 长期存活的对象将进入老年代 动态对象年龄判定 空间分配担保 2. 垃圾 ...
- Java虚拟机-垃圾收集器
垃圾收集器(Garbage Collection, GC)的诞生引导出了三个问题: 哪些内存需要回收? 什么时候回收? 如何回收? 对于线程独占的三个区域(程序计数器.虚拟机栈.本地方法栈)不用过多的 ...
- Java虚拟机—垃圾收集器(整理版)
1.概述 如果说收集算法是内存回收的方法论,那么垃圾收集器就是内存回收的具体实现.Java虚拟机规范中对垃圾收集器应该如何实现并没有规定,因此不同的厂商.不同版本的虚拟机所提供的垃圾收集器都可能会有很 ...
- Java虚拟机--垃圾收集器和内存分配
垃圾收集器和内存分配 程序计数器.虚拟机栈.本地方法栈这三个区域和线程的生命周期一致,所以方法结束或者线程结束时,内存自然就跟着回收了.Java堆和方法区,只有在程序处于运行期间才能知道会创建哪些对象 ...
- 深入理解JAVA虚拟机 垃圾收集器和内存分配策略
引用计数算法 很多教科书判断对象是否存活的算法是这样的:给对象中添加一个引用计数器,每当有一个地方引用它时,计数器值就加1:当引用失效时,计数器值就减1:任何时刻计数器都为0的对象就是不可能再被使用的 ...
- Java虚拟机 垃圾收集器与内存分配策略
说起GC,我们要思考的主要有三件事 哪些内存需要回收 那些已经“死去”的对象,那么哪些对象“死”,哪些对象“活”呢,有个简单的办法 引用计数法,但是没法解决循环依赖问题 所以Java虚拟机采用的是可达 ...
随机推荐
- 简单易用的堡垒机系统—Teleport
简单易用的堡垒机系统-Teleport 官方文档:http://teleport.eomsoft.net/doc#!1 一.Teleport介绍 Teleport是触维软件推出的一款简单易用的堡垒机 ...
- iTools(pro)下载
http://bbs.feng.com/forum.php?mod=viewthread&tid=10225990&page=1&extra=#pid157941878 htt ...
- echarts雷达图点击事件 包含(2.x,3.85,4.02)测试
最近看见别人问的问题,点击雷达图的拐点,获取点击数据的问题,直接上代码. echarts 2x 的点击事件 echarts配置问题:https://www.douban.com/note/509404 ...
- Testlink1.9.17使用方法(第十三章 使用中遇到的问题)
第十三章 使用中遇到的问题 一. 登录Testlink后,新建一个项目后,会出现如下提示: 解决办法:打开Testlink安装文件夹下的config.inc.php文件, 原来:$tlCfg-> ...
- java中的数据类型,运算符,字符串,输入输出,控制流,大数值,数组; 《java核心技术卷i》 第三章:java基本程序结构;
<java核心技术卷i> 第三章:java基本程序结构: 每次看书,去总结的时候,总会发现一些新的东西,这次对于java的数组有了更深的了解: java中的数据类型,运算符,字符串,输入输 ...
- Apktool(3)——Apktool的使用
一.apktool的作用 安卓应用apk文件不仅仅是包含有resource和编译的java代码的zip文件,如果你尝试用解压工具(如好压)解压后,你将会获得classes.dex和resource.a ...
- 如何设置访问内网web项目
1.若我的项目搭建在一个linux虚拟机上 2.在内网的一台电脑做以下配置 3.测试访问 ******************************************************* ...
- Scala类型限定
package big.data.analyse.scala /** * 类型限定 * Created by zhen on 2018/12/9. */ object Lxxd { def main( ...
- 简化OSI七层网络协议
OSI层 功能 TCP/IP协议 设备 应用层 文件传输,电子邮件,文件服务,虚拟终端 TFTP,HTTP,SNMP,FTP,SMTP,DNS,Telnet 表示层 数据格式化,代码转换,数据解密 会 ...
- C#-结构体(十)
结构体概念 在C#中,结构体是值类型,一般适用于表示类似Point.Rectangle.Color的对象 值类型能够降低对堆的管理.使用.降低垃圾回收,表现出更好的性能.可是值类型也有不好的一面.会涉 ...