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 ...
随机推荐
- python基础之删除文件及删除目录的方法
下面来看一下python里面是如何删除一个文件及文件夹的~~ 1 2 3 4 5 6 7 8 #首先引入OS模块 import os #删除文件: os.remove() #删除空目录: os.r ...
- 软工实践Beta冲刺(6/7)
队名:起床一起肝活队 组长博客:博客链接 作业博客:班级博客本次作业的链接 组员情况 组员1(队长):白晨曦 过去两天完成了哪些任务 描述: 1.界面的修改与完善 展示GitHub当日代码/文档签入记 ...
- java正则表达式 3 -- 查找
用正则表达式执行查找命令,则需要用正则对象,其规则和执行顺序如下: 指定为字符串的正则表达式必须首先被便以为此类的实例.然后,可将得到的正则对象匹配任意的字符串用于创建Mather对象,执行匹配所涉及 ...
- Kernel Mode, User Mode
之前关于kernel mode,user mode之间的切换,有个问题一直有些疑惑. 一个进程有没有办法,从user mode切换到kernel mode去执行自己的代码.我知道答案肯定是不行,但是为 ...
- [洛谷P3153] [CQOI2009]跳舞
题目大意:有n个女生,n个男生,每次一男一女跳舞.同一队只会跳一次.每个男孩最多只愿意和k个不喜欢的女孩跳舞,女孩同理.问舞会最多能有几首舞曲? 题解:二分跳了多少次舞,每次重建图,建超级原点和汇点, ...
- Conjugate 解题报告
Conjugate 问题描述 在不存在的 \(\text{noip day3}\) 中,小 \(\text{w}\) 见到了一堆堆的谜题. 比如这题为什么会叫共轭? 他并不知道答案. 有 \(n\) ...
- 如何使用Navicat备份数据库脚本
Navicat是一个实用的工具,可以用来备份数据库(Oracle.MySQL.SQLServer)脚本. 备份步骤如下: 1.打开已建立的数据库连接,鼠标右键点击,选择[转储SQL文件]->[结 ...
- D. Equalize the Remainders (set的基本操作)
D. Equalize the Remainders time limit per test 3 seconds memory limit per test 256 megabytes input s ...
- c++对拍实现
直接上代码吧. #include<bits/stdc++.h> using namespace std; int main(){ while(1){ system("./cute ...
- 【BZOJ1857】【SCOI2010】传送带 [三分]
传送带 Time Limit: 1 Sec Memory Limit: 64 MB[Submit][Status][Discuss] Description 在一个2维平面上有两条传送带,每一条传送 ...