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虚拟机采用的是可达 ...
随机推荐
- MySQL 慢查询日志配置与简析
MySQL慢查询日志配置与简析 By:授客 QQ:1033553122 <1> 查看是否开启慢查询日志 SHOW VARIABLES LIKE 'slow%'; 说明: a. 如果sl ...
- 四. Redis事务处理
Redis目前对事务的支持还是比较简单,Redis能保证一个Client发起的事务中的命令可以连续执行,而中间不会插入其他Client的命令:当一个Client在连接中发起一个multi命令的时候,这 ...
- OkHttp的缓存
看到很多小伙伴对OkHttp的缓存问题并不是十分了解,于是打算来说说这个问题.用好OkHttp中提供的缓存,可以帮助我们更好的使用Retrofit.Picasso等配合OkHttp使用的框架.OK,废 ...
- Android-启动页“android:windowBackground”变型?
<?xml version="1.0" encoding="utf-8"?> <layer-list xmlns:android=" ...
- [20190213]学习bbed-恢复删除的数据.txt
[20190213]学习bbed-恢复删除的数据.txt --//以前也做过类似测试,当时在用bbed做verify时错误都不处理,当时的想法就是能读出就ok了.--//而且当时也做成功,纯粹是依葫芦 ...
- [20170611]关于数据块地址的计算.txt
[20170611]关于数据块地址的计算.txt --//如果数据库出现一些问题,会在alert或者跟踪文件,或者屏幕出现一些错误提示.例如: ORA-00600: internal error co ...
- Eclipse引入spring约束详细教程
1.打开eclipse的window-preferences,搜索catalog. 2.点击add,点击File System,弹出页面选择spring-beans-4.2.xsd. 3.key ty ...
- django安装与使用
django安装与使用 --更新中 安装 我这里采用pip安装 pip install django 创建django工程 创建好的工程,会在当前目录.下 django-admin startproj ...
- 网站出现403 Forbidden
1, 你在一定时间内过多地访问此网站(一般是用采集程序),被防火墙拒绝访问了 2, 网站域名解析到了空间,但空间未绑定此域名 3, 你的网页脚本文件在当前目录下没有执行权限 4, 服务器繁忙,同一IP ...
- Markdown编辑器开发记录(一):开发的初衷和初期踩的坑
先说下选择Markdown编辑器的原因,我们进行平台开发,需要很多的操作手册和API文档,要在网站中展示出来就需要是HTML格式的文件,但是由于内容很多,不可能全部由技术人员进行文档的编写,如果是只有 ...