JVM GC算法
在判断哪些内存需要回收和什么时候回收用到GC 算法,本文主要对GC 算法进行讲解。
JVM垃圾判定算法
常见的JVM垃圾判定算法包括:引用计数算法、可达性分析算法。
引用计数算法(Reference Counting)
引用计数算法是通过判断对象的引用数量来决定对象是否可以被回收。
给对象中添加一个引用计数器,每当有一个地方引用它时,计数器值就加1;当引用失效时,计数器值就减1;任何时刻计数器为0的对象就是不可能再被使用的。
优点:简单,高效,现在的objective-c用的就是这种算法。
缺点:很难处理循环引用,相互引用的两个对象则无法释放。因此目前主流的Java虚拟机都摒弃掉了这种算法。
举个简单的例子,对象objA和objB都有字段instance,赋值令objA.instance=objB及objB.instance=objA,除此之外,这两个对象没有任何引用,实际上这两个对象已经不可能再被访问,但是因为互相引用,导致它们的引用计数都不为0,因此引用计数算法无法通知GC收集器回收它们。
public class ReferenceCountingGC {
public Object instance = null;
public static void main(String[] args) {
ReferenceCountingGC objA = new ReferenceCountingGC();
ReferenceCountingGC objB = new ReferenceCountingGC();
objA.instance = objB;
objB.instance = objA;
objA = null;
objB = null;
System.gc();//GC
}
}
运行结果
[GC (System.gc()) [PSYoungGen: 3329K->744K(38400K)] 3329K->752K(125952K), 0.0341414 secs] [Times: user=0.00 sys=0.00, real=0.06 secs]
[Full GC (System.gc()) [PSYoungGen: 744K->0K(38400K)] [ParOldGen: 8K->628K(87552K)] 752K->628K(125952K), [Metaspace: 3450K->3450K(1056768K)], 0.0060728 secs] [Times: user=0.05 sys=0.00, real=0.01 secs]
Heap
PSYoungGen total 38400K, used 998K [0x00000000d5c00000, 0x00000000d8680000, 0x0000000100000000)
eden space 33280K, 3% used [0x00000000d5c00000,0x00000000d5cf9b20,0x00000000d7c80000)
from space 5120K, 0% used [0x00000000d7c80000,0x00000000d7c80000,0x00000000d8180000)
to space 5120K, 0% used [0x00000000d8180000,0x00000000d8180000,0x00000000d8680000)
ParOldGen total 87552K, used 628K [0x0000000081400000, 0x0000000086980000, 0x00000000d5c00000)
object space 87552K, 0% used [0x0000000081400000,0x000000008149d2c8,0x0000000086980000)
Metaspace used 3469K, capacity 4496K, committed 4864K, reserved 1056768K
class space used 381K, capacity 388K, committed 512K, reserved 1048576K
Process finished with exit code 0
从运行结果看,GC日志中包含“3329K->744K”,意味着虚拟机并没有因为这两个对象互相引用就不回收它们,说明虚拟机不是通过引用技术算法来判断对象是否存活的。
可达性分析算法(根搜索算法)
可达性分析算法是通过判断对象的引用链是否可达来决定对象是否可以被回收。
从GC Roots(每种具体实现对GC Roots有不同的定义)作为起点,向下搜索它们引用的对象,可以生成一棵引用树,树的节点视为可达对象,反之视为不可达。
在Java语言中,可以作为GC Roots的对象包括下面几种:
- 虚拟机栈(栈帧中的本地变量表)中的引用对象。
- 方法区中的类静态属性引用的对象。
- 方法区中的常量引用的对象。
- 本地方法栈中JNI(Native方法)的引用对象
真正标记以为对象为可回收状态至少要标记两次。
四种引用
强引用就是指在程序代码之中普遍存在的,类似"Object obj = new Object()"这类的引用,只要强引用还存在,垃圾收集器永远不会回收掉被引用的对象。
Object obj = new Object();
软引用是用来描述一些还有用但并非必需的对象,对于软引用关联着的对象,在系统将要发生内存溢出异常之前,将会把这些对象列进回收范围进行第二次回收。如果这次回收还没有足够的内存,才会抛出内存溢出异常。在JDK1.2之后,提供了SoftReference类来实现软引用。
Object obj = new Object();
SoftReference<Object> sf = new SoftReference<Object>(obj);
弱引用也是用来描述非必需对象的,但是它的强度比软引用更弱一些,被弱引用关联的对象,只能生存到下一次垃圾收集发生之前。当垃圾收集器工作时,无论当前内存是否足够,都会回收掉只被弱引用关联的对象。在JDK1.2之后,提供了WeakReference类来实现弱引用。
Object obj = new Object();
WeakReference<Object> wf = new WeakReference<Object>(obj);
虚引用也成为幽灵引用或者幻影引用,它是最弱的一中引用关系。一个对象是否有虚引用的存在,完全不会对其生存时间构成影响,也无法通过虚引用来取得一个对象实例。为一个对象设置虚引用关联的唯一目的就是能在这个对象被收集器回收时收到一个系统通知。在JDK1.2之后,提供给了PhantomReference类来实现虚引用。
Object obj = new Object();
PhantomReference<Object> pf = new PhantomReference<Object>(obj);
JVM垃圾回收算法
常见的垃圾回收算法包括:标记-清除算法,复制算法,标记-整理算法,分代收集算法。
在介绍JVM垃圾回收算法前,先介绍一个概念。
Stop-the-World
Stop-the-world意味着 JVM由于要执行GC而停止了应用程序的执行,并且这种情形会在任何一种GC算法中发生。当Stop-the-world发生时,除了GC所需的线程以外,所有线程都处于等待状态直到GC任务完成。事实上,GC优化很多时候就是指减少Stop-the-world发生的时间,从而使系统具有高吞吐 、低停顿的特点。
标记—清除算法(Mark-Sweep)
之所以说标记/清除算法是几种GC算法中最基础的算法,是因为后续的收集算法都是基于这种思路并对其不足进行改进而得到的。标记/清除算法的基本思想就跟它的名字一样,分为“标记”和“清除”两个阶段:首先标记出所有需要回收的对象,在标记完成后统一回收所有被标记的对象。
标记阶段:标记的过程其实就是前面介绍的可达性分析算法的过程,遍历所有的GC Roots对象,对从GC Roots对象可达的对象都打上一个标识,一般是在对象的header中,将其记录为可达对象;
清除阶段:清除的过程是对堆内存进行遍历,如果发现某个对象没有被标记为可达对象(通过读取对象header信息),则将其回收。
不足:
- 标记和清除过程效率都不高
- 会产生大量碎片,内存碎片过多可能导致无法给大对象分配内存。
复制算法(Copying)
将内存划分为大小相等的两块,每次只使用其中一块,当这一块内存用完了就将还存活的对象复制到另一块上面,然后再把使用过的内存空间进行一次清理。
现在的商业虚拟机都采用这种收集算法来回收新生代,但是并不是将内存划分为大小相等的两块,而是分为一块较大的 Eden 空间和两块较小的 Survior 空间,每次使用 Eden 空间和其中一块 Survivor。在回收时,将 Eden 和 Survivor 中还存活着的对象一次性复制到另一块 Survivor 空间上,最后清理 Eden 和 使用过的那一块 Survivor。HotSpot 虚拟机的 Eden 和 Survivor 的大小比例默认为 8:1,保证了内存的利用率达到 90 %。如果每次回收有多于 10% 的对象存活,那么一块 Survivor 空间就不够用了,此时需要依赖于老年代进行分配担保,也就是借用老年代的空间。
不足:
- 将内存缩小为原来的一半,浪费了一半的内存空间,代价太高;如果不想浪费一半的空间,就需要有额外的空间进行分配担保,以应对被使用的内存中所有对象都100%存活的极端情况,所以在老年代一般不能直接选用这种算法。
- 复制收集算法在对象存活率较高时就要进行较多的复制操作,效率将会变低。
标记—整理算法(Mark-Compact)
标记—整理算法和标记—清除算法一样,但是标记—整理算法不是把存活对象复制到另一块内存,而是把存活对象往内存的一端移动,然后直接回收边界以外的内存,因此其不会产生内存碎片。标记—整理算法提高了内存的利用率,并且它适合在收集对象存活时间较长的老年代。
不足:
效率不高,不仅要标记存活对象,还要整理所有存活对象的引用地址,在效率上不如复制算法。
分代收集算法(Generational Collection)
分代回收算法实际上是把复制算法和标记整理法的结合,并不是真正一个新的算法,一般分为:老年代(Old Generation)和新生代(Young Generation),老年代就是很少垃圾需要进行回收的,新生代就是有很多的内存空间需要回收,所以不同代就采用不同的回收算法,以此来达到高效的回收算法。
新生代:由于新生代产生很多临时对象,大量对象需要进行回收,所以采用复制算法是最高效的。
老年代:回收的对象很少,都是经过几次标记后都不是可回收的状态转移到老年代的,所以仅有少量对象需要回收,故采用标记清除或者标记整理算法。
JVM GC算法的更多相关文章
- JVM GC算法 CMS 详解(转)
前言 CMS,全称Concurrent Low Pause Collector,是jdk1.4后期版本开始引入的新gc算法,在jdk5和jdk6中得到了进一步改进,它的主要适合场景是对响应时间的重要性 ...
- jvm gc 算法
1标记-清除法 他是现代垃圾回收算法的思想基础. 标记-清除算法将垃圾回收分为两个阶段:标记阶段和清除阶段. 在标记阶段,首先通过根节点,标记所有从根节点开始的可达对象(根搜索算法).而未被标记的对象 ...
- JVM GC 算法原理(转)
出处: https://mp.weixin.qq.com/s/IfUFuwn8dsvMIhTS3V01FA 对于JVM的垃圾收集(GC),这是一个作为Java开发者必须了解的内容,那么,我们需要去了解 ...
- JVM GC算法 垃圾回收器
JVM的垃圾回收算法有三种: 1.标记-清除(mark-sweep):啥都不说,直接上图 2.标记-整理(mark-compact) 3.复制(copy) 分代收集算法 ...
- jvm GC算法和种类
1.GC 垃圾收集 Garbage Collection 通常被称为“GC”,它诞生于1960年 MIT 的 Lisp 语言,经过半个多世纪,目前已经十分成熟了. jvm 中,程序计数器.虚拟 ...
- 【转载】Java性能优化之JVM GC(垃圾回收机制)
文章来源:https://zhuanlan.zhihu.com/p/25539690 Java的性能优化,整理出一篇文章,供以后温故知新. JVM GC(垃圾回收机制) 在学习Java GC 之前,我 ...
- Java性能优化之JVM GC(垃圾回收机制)
Java的性能优化,整理出一篇文章,供以后温故知新. JVM GC(垃圾回收机制) 在学习Java GC 之前,我们需要记住一个单词:stop-the-world .它会在任何一种GC算法中发生.st ...
- 46张PPT讲述JVM体系结构、GC算法和调优
本PPT从JVM体系结构概述.GC算法.Hotspot内存管理.Hotspot垃圾回收器.调优和监控工具六大方面进行讲述.(内嵌iframe,建议使用电脑浏览) 好东西当然要分享,PPT已上传可供下载 ...
- JVM学习(4)——全面总结Java的GC算法和回收机制
俗话说,自己写的代码,6个月后也是别人的代码……复习!复习!复习!涉及到的知识点总结如下: 一些JVM的跟踪参数的设置 Java堆的分配参数 -Xmx 和 –Xms 应该保持一个什么关系,可以让系统的 ...
随机推荐
- JSP标签c:forEach实例
JSP标签c:forEach实例 1.实例源码 <%@ page language="java" import="java.util.*" pageEnc ...
- Caused by: java.lang.ClassNotFoundException: Could not load requested class : org.h2.Driver
1.错误描述 WARN:2015-05-01 13:26:10[localhost-startStop-1] - HHH000402: Using Hibernate built-in connect ...
- hdu5860 Death Sequence
这题一开始写的线段数是从中间开始查找 k个 导致是nlogn 每次查找应该都是从头找每次找的个数不同就好了 还有一种递推的写法我放下面了 #include<bits/stdc++.h> u ...
- Visio如何调整锁定图像大小
在Visio中,比如模板中的UML类图,是不可调整大小的,这可能给我们设计图片带来了一些不便之处,如下图: 可以看到其边框是显示锁定状态无法修改的,当我们在左下角修改器长宽时,也会出现不可修改的情况. ...
- java SpringWeb 接收安卓android传来的图片集合及其他信息入库存储
公司是做APP的,进公司一年了还是第一次做安卓的接口 安卓是使用OkGo.post("").addFileParams("key",File); 通过这种方式传 ...
- string的常见用法
使用前提:需要头文件! #include<string> using namespace std; 1.string类型是可以下标访问的,也可以通过迭代器访问 string::iterat ...
- TensorLayer官方中文文档1.7.4:API – 数据预处理
所属分类:TensorLayer API - 数据预处理¶ 我们提供大量的数据增强及处理方法,使用 Numpy, Scipy, Threading 和 Queue. 不过,我们建议你直接使用 Tens ...
- java继承属性相关介绍
这个只需要记住一点,父类的任何属性(变量可以看做属性),子类均可继承并覆盖,allType(father)-->changeAnyType(son)-->AnyType 这是父类的所有代表 ...
- 【BZOJ1087】【SCOI2005】互不侵犯(状态压缩,动态规划)
题面 这种傻逼题懒得粘贴了... 题解 傻逼题 \(f[i][j][k]\)表示当前第\(i\)列,当前放置状态为\(j\),已经放了\(k\)个 暴力判断状态合法性,暴力判断转移合法性,然后统计答案 ...
- [BZOJ1601] [Usaco2008 Oct] 灌水 (kruskal)
Description Farmer John已经决定把水灌到他的n(1<=n<=300)块农田,农田被数字1到n标记.把一块土地进行灌水有两种方法,从其他农田饮水,或者这块土地建造水库. ...