JVM垃圾收集规则和算法
1.垃圾收集 Garbage Collection
- 程序计数器、虚拟机栈、本地方法栈这三部分内存随着线程生而生,随着线程灭而自然的回收,他们的大小在编译期间就大致确定了下来,所以对这部分的回收是具备确定性的。
- Java堆,方法区则不一样,在运行期间会创建对象,对象内存分配和回收都是动态的,所以是不确定的,具备随机性。
2.判断对象已死
- 引用计数法: 有一个地方引用它就加1,引用失效就减1,为0就进行回收,但有一缺点就是无法解决对象间循环引用问题。Python使用了引用计数法,主流的jvm都没使用引用计数法。
- 可达性分析: 通过GC Roots的对象作为起点向下搜索,经过的路径叫做 引用链。当一个对象通过引用链到达不了,则这个对象不可达,将会被回收。
- 因为主要GC在堆和方法区工作,主要清理这部分的对象。对象是由线程启动之后创建的,随着线程退出一些对象就不可用了,线程退出意味着方法的退出,而方法和jvm栈、方法区、本地方法栈都有关系,因此要从这些地方开始向下搜索引用链,因此GC Roots对象包括以下几种。
- 虚拟机栈中引用的对象,就是那些在栈帧的本地变量表中引用的对象
- 方法区中类成员引用的对象
- 方法区中常量引用的对象。
- 本地方法栈中JNI引用的对象,即Native方法中引用的对象。
3.引用
- 强引用,就是一般的引用。
- 软引用,SoftReference类来实现。在将要发生内存溢出前回收。
- 弱引用,WeakReference类来实现,只能存活到下一次垃圾回收前,下一次垃圾回收时被回收。
- 虚引用,也称幽灵引用,回收对象时的一个状态,该对象肯定被回收,作用就是给系统一个通知我要被回收了。
4.不可达对象与回收
- 不可达对象不一定会被回收,在回收之前至少要经历2次标记过程。
- 发现对象不可达标记一次,并进行一个筛选。
- 筛选是为了选出覆写了finalize()方法且该方法还未被jvm调用的对象,并把它们放入一个F-Queue队列中,该队列就是用来执行finalize方法的,并且由一个低优先级的Finalizer线程执行finalize()方法,但不保证finalize()方法执行完,只保证每个对象执行一次finalize()方法。
- 如果没覆写或者覆写了但调用了则不会被放入F-Queue队列中,因为finalize方法只执行一次。
- 自救:在finalize方法中如果重新将本身(this)赋值给某个类变量和某个类的成员变量则被第二次标记时会被移出“即将回收集合”,finalize方法时对象自救的唯一机会。
- 此时对在F-Queue队列中未能自救成功的对象进行第二次标记,被标记2次意味着这个对象即将死亡。
- 因为finalize方法肯定会执行一次所以可利用此特性做一些操作,但是不建议这么做,因为他不一定能执行完,它可以做的try-finally也可以做。
5.回收方法区
- jvm规范不要求方法区一定实现垃圾回收,但是也是存在垃圾收集,但是效率太低因为只回收废弃常量和无用类(进行卸载类),但回收这些可以被可以回收的对象性价比太低,还不如不回收。
- 回收常量和回收对象类似。
- 回收类(Class对象)即卸载类比较苛刻
- Java堆中不存在任何实例。
- 加载类的classLoader 已经被回收
- 该类对应的java.lang.Class对象没有在任何地方被引用,无法在任何地方通过反射访问该类的方法
- 即使满足这三个条件也不一定被回收,只是具备了被回收的可能。
- Hotspot 提供了参数来控制是否被回收,在大量使用反射 动态代理 等等情况下会生成大量类,此时就用参数要控制进行卸载类,保证方法区不会溢出。
JVM提供了几个参数控制类回收:
- -Xnoclassgc:关闭CLASS的垃圾回收功能
- -verbose:class:在控制台查看类加载情况
- -XX:+TraceClassLoading:查看类加载信息
- -XX:+TraceClassUnLoading : 查看类卸载信息(FastDebug版的虚拟机才支持)
6.垃圾回收算法
- 标记-清除算法 Mark-Sweep
- 先标记出需要清理的对象然后再统一回收。这是最基础的算法,其他都是他的改进型。
- 不足之处: 标记和回收效率都不高,而且清理之后会产生大量的不连续的内存碎片,一旦要分配一个大内存对象时不得不提前触发一次垃圾收集。
- 复制算法: Copy
- 将内存分为大小相等的部分,每次只使用一块,满了之后将存活对象复制到另一块,再清除之前那块。
- 如果将内存分为大小相等的2部分,那么每次只能使用一部分代价太大。
- 商业虚拟机都是使用这种算法来回收 新生代,新生代中98%的对象存活时间非常短,所以并没有按照1:1来分割,而是按照8:1:1分割成1块Eden和2块Survivor,新生代可使用区是Eden和一块Survivor,这样只有10%空闲。
- 每次将Eden和其中一块Survivor中存活的对象复制到另一块上,如果Survivor不够用那么会将存活对象分配到老年代上。
- Survivor存在的作用就是减少向老年代分配的对象,如果没有Survivor那么每次GC都要把存活对象放入老年代,此时新生代空空如也而老年代很快就会满,从而触发Full GC 更加耗费时间。
- 如果1块Survivor那么会将Eden存活的对象复制到Survivor上,Survivor上存活的对象还在原处,这样Survivor就存在了碎片,如果是2块Survivor那Eden和其中一块Survivor中存活的对象移到另一块上就不会存在碎片。
- 标记-整理
- 复制算法在存活对象比较多的情况下效率就会减低所以它适合新生代,不适合老年代。
- 标记后先不对可回收对象进行回收而是让存活对象向一端移动,然后清理边界以外的内存。
- 由于老年代存活对象多,所以移动的概率小反而效率高,如果存活对象少那么移动的代价就大了。
- 标记-整理算法实现和优化
- 分代收集
- Java把堆分为新生代和老年代,新生代中有大量对象死去所以适合复制算法,老年代有大量对象存活所以适合标记-清理,标记-整理进行回收。
7.HotSpot算法实现
枚举根节点
- 要从GC Roots节点开始查找引用链就得确定哪些引用(Reference对象)可以作为GC Roots,而存在可以作为GC Roots 的内存区域有很多其他类型数据,逐个检查势必耗费大量时间。
- HotSpot中用OopMap的数据结构来存放引用信息,避免检查全部数据。
Stop The World
- 要确定GC Roots 首先要让系统停顿,不能出现引用还在变化的情况,否则可达性分析(确定GC Roots 和查找引用链)结果准确性无法得到保证,这点导致GC进行时必须停止所有Java线程的一个重要原因,所以又称 Stop The World。即使号称不会发生停顿的GMS收集器中枚举根节点时也会发生 停顿。
- 停顿下来并不需要一个不漏的检查引用,因为虚拟机知道哪些地方存放着对象引用。
- HotSpot中用OopMap的数据结构来存放“什么地方有引用”的信息,在类加载过程中及以后的JIT编译过程中执行到特定位置时会计算出引用信息保存到对应OopMap中。
- 特定位置就是 安全点
安全点 Safepoint
- HotSpot并不需要为每一个指令生成信息保存在OopMap中,而是在特定位置计算出 引用信息 并保存到OopMap中,这个特定位置就是安全点。
- 程序执行时并不是任何位置都可以停下来GC,而是到达安全点才可以暂停。
- 安全点的特征就是之后可能程序会长时间执行,如方法调用,循环跳转,异常跳转,这些指令就会产生安全点。
- 要想GC就要让所有线程都到达安全点,这有2种方式实现
- 抢断式中断: 主动中断所有线程,发现中断点不是安全点则恢复线程,让他跑到安全点。 几乎没有虚拟机使用抢断式中断。
- 主动中断: 给每个线程设置一个中断标志,中断标志就在安全点处,线程发现中断标志位true则挂起线程
安全区 Safe Region
- 如果线程处于sleep或者blocked,那么他就无法走到安全点,GC时会标记一个 Safe Region ,在此区域任何地方开始GC都是安全的,当线程被唤醒继续执行进入Safe Region再离开时会检查GC是否完成,如果完成则离开,如果没有完成则暂时不离开安全区。
8.垃圾收集器
9.内存分配和回收
JVM垃圾收集规则和算法的更多相关文章
- JVM垃圾收集策略与算法
垃圾收集策略与算法 程序计数器.虚拟机栈.本地方法栈随线程而生,也随线程而灭:栈帧随着方法的开始而入栈,随着方法的结束而出栈.这几个区域的内存分配和回收都具有确定性,在这几个区域内不需要过多考虑回收的 ...
- JVM垃圾收集算法(标记-清除、复制、标记-整理)
[JVM垃圾收集算法] 1)标记-清除算法: 标记阶段:先通过根节点,标记所有从根节点开始的对象,未被标记的为垃圾对象(错了吧?) 清除阶段:清除所有未被标记的对象 2)复制算法: 将原有的内存空间 ...
- JVM垃圾收集算法
JVM垃圾收集 1. 判断对象是否存活 引用计数算法 对象添加一个引用计数器,每个地方引用它,计数器值加+1:当引用失效,计算器值减1:任何时刻计数器为0的对象不可能被使用.引用计数算法实现简单,高效 ...
- JVM垃圾收集算法之标记算法
前言 总所周知,jvm的垃圾收集算法一般包括标记.清除.整理三个阶段,最近在看了有关于垃圾收集的标记算法,记录一下自己的理解. 垃圾收集中标记算法有两种:一种是引用计数法,一种是根搜索算法. 引用记数 ...
- JVM中的GC算法,JVM参数,垃圾收集器分类
一.在JVM中什么是垃圾?如何判断一个对象是否可被回收?哪些对象可以作为GC Roots的根 垃圾就是在内存中已经不再被使用到的空间就是垃圾. 1.引用计数法: 内部使用一个计数器,当有对象被引用+1 ...
- JVM——垃圾收集算法及垃圾回收器
一.垃圾回收算法 1.标记-清除算法 1)工作流程 算法分为"标记"和"清除"阶段:首先标记出所有需要回收的对象(标记阶段),在标记完成后统一回收所有被标记的对 ...
- JVM学习七-(复习)垃圾收集策略与算法
垃圾收集策略与算法 程序计数器.虚拟机栈.本地方法栈随线程而生,也随线程而灭:栈帧随着方法的开始而入栈,随着方法的结束而出栈.这几个区域的内存分配和回收都具有确定性,在这几个区域内不需要过多考虑回收的 ...
- 【004】【JVM——垃圾收集算法】
Java虚拟机学习总结文件夹 垃圾收集算法 垃圾收集算法的实现涉及大量的程序细节,并且各个平台的虚拟机操作内存的方法又各不同样,介绍几种垃圾收集算法的思想及其发展过程. 标记-清除算法 垃圾收集 ...
- 7种JVM垃圾收集器特点,优劣势、及使用场景
今天继续JVM的垃圾回收器详解,如果说垃圾收集算法是JVM内存回收的方法论,那么垃圾收集器就是内存回收的具体实现. 一.常见的垃圾收集器有3类 1.新生代的收集器包括 Serial PraNew Pa ...
随机推荐
- php+Mysql中网页出现乱码的解决办法详解
$conn = mysql_connect("$host","$user","$password");mysql_query("S ...
- 持久化ORM框架——Hibernate与mybatis
最初SUN公司推出了JavaEE服务器端组件模型(EJB),但是由于EJB配置复杂,且适用范围较小,于是很快就被淘汰了.与EJB的失败伴随而来的是另外一个框架的应运而生.他就是至今也比较流行的Hibe ...
- Spring 学习笔记(八)—— 注解使用整合
@Autowired —— 自动装配 需先在配置文件中,配置一个org.springframework.beans.factory.annotation. AutowiredAnnotationBe ...
- Delphi中取得程序版本号
Delphi做的程序,如果想包含版本信息, 必须在Delphi的集成编辑环境的菜单“Project/Options/Version Info”里面添加版本信息.即在Version Info 选项卡中选 ...
- 【bzoj3931】[CQOI2015]网络吞吐量 最短路+最大流
题目描述 路由是指通过计算机网络把信息从源地址传输到目的地址的活动,也是计算机网络设计中的重点和难点.网络中实现路由转发的硬件设备称为路由器.为了使数据包最快的到达目的地,路由器需要选择最优的路径转发 ...
- BZOJ4592 SHOI2015脑洞治疗仪(线段树)
考虑需要资瓷哪些操作:区间赋值为0:统计区间1的个数:将区间前k个0变为1:询问区间最长全0子串.于是线段树维护区间1的个数.0的个数.最长前缀后缀全0子串即可.稍微困难的是用一个log实现将区间前k ...
- AGC018C Coins (set)
题目大意: 给出n个人,每个人手里都有xi个金牌,yi个银牌,ci个铜牌. 你需要选出X个人,拿走他们手里的金牌,选出Y个人,拿走他们手里的银牌,选出Z个人,拿走他们手里的铜牌 X+Y+Z = n.并 ...
- IHE PIX规范
IHE(Integrating Healthcare Enterprise) 集成医疗企业 IHE概念是由医学专家和广大医护工作者.相关政府部门.信息技术专家和企业共同发起的,目的是提供一种更好的方法 ...
- POJ3020:Antenna Placement(二分图匹配)
Antnna Placement Time Limit: 1000MS Memory Limit: 65536K Total Submissions: 11093 Accepted: 5459 ...
- Google MapReduce中文版
英文原文链接: Google Map Reduce 译文原文链接: Google MapReduce中文版 Google MapReduce中文版 译者: alex 摘要 MapReduce是一个编程 ...