java 垃圾回收机制以及经典垃圾回收器详解

判断对象存活方法

引用计数法:在对象中添加一个引用计数子,每当一个地方引用他时,计数器就加一,当引用失效时,计数器就减一。

会有对象循环引用问题:

objA.instance = objB
objB.instance = objA

objA 有objB 的引用 objB 有 objA 的引用,他们相互引用着对方。导致他们无法回收。

可达性分析:

从GC Roots 根对象作为起点,根据引用关系向下搜索,如果对象可达,就说明对象存活,如果对象不可达,就说明对象可以被回收。

GC Roots的根对象为:

1)在虚拟机栈 栈帧中的 本地变量表 中引用的对象

2)方法区静态属性引用的对象

3)方法区常量引用 的对象,如字符串常量池中的引用

4)本地方法栈中JNI引用的对象

5)虚拟机内部引用的对象,如基本数据类型对应的Class对象,一些常驻的异常对象等,还有系统类加载器

6)被同步锁持有的对象

收集线程和用户线程在并发可达性分析

并发 的可达性分析,由于用户线程会即时修改对象的引用关系, 可能会造成两种异常:

1)原本消亡的对象错误标记为存活,这个可以接受,就造成浮动垃圾,下一次收集即可。

2)原本存活的对象 标记为 消失。

三色法分析图:

黑色:已经扫描过的对象

灰色:已经访问过,但还有一个引用没有被扫描

白色:为被访问过,若到最后还是白色,说明此对象是需要回收的。

黑色误标记为白色有两个条件

1)复制器插入一条或多条从黑色对象到白色对象的新引用

2)复制器删除了全部从灰色对象到该白色对象的直接或间接的引用

解决方法:

1)增量更新,破坏条件1,把黑色对象对白色对象的新增引用记录下来,等并发扫描结束后,再以这些对象出发重新扫描。

2)原始快照(SATB),破解条件2,当灰色对象要删除对白色对象的引用关系时,记录下来。并发结束后再以记录节点开始重新扫描。

分代收集

堆:

新生代(1/3) 老年代(2/3)

新生代 分为 Eden/From/To

新生代存放:比较小,时长比较小

老年代:比较大 存放时长比较大

轻GC

重GC(full GC) -> STW(停止事件),fallGc特别费资源

Eden -> from <-> To -> old

对象在from和to循环15(默认)次之后,会放到老年代

垃圾收集算法

标记-清除 算法:

算法分为标记 和 清除两个阶段,首先标记出所需要回收的对象,标记完成之后,统一回收所标记的对象。

优点:最基础的算法,实现简单

缺点:1)执行效率不稳定,对象越多,效率越低

2)内存碎片化,需要分配大对象时可能无足够连续的空间。

代表垃圾收集器:

标记复制算法(复制算法):

把内存分为大小相等的两块,每次只使用其中一款,当一块快用完时,它将存活的对象复制到另一块上。

优点:能产生连续的空间

缺点:耗内存,对象存活率较高时,效率会降低(不适合老年代)。

代表垃圾收集器: 很多新生代的回收,都用这种算法。

标记整理 算法

首先标记出所需要的对象,标记完成后,对存活对象移动到内存的一段,然后清除边界外的对象。

优点:有连续的内存空间;系统吞吐量(用户线程和收集器的效率总和)会提高。

缺点:整理内存耗时会比较大,会造成 “Stop The World”;

代表垃圾收集器:Parallel Scavenge收集器

CMS中主要用标记清除算法,但是当内存碎片化到影响对象分配时,就会使用一次标记整理算法 去整理内存碎片。

垃圾收集器

Serial收集器

最基础最悠久的垃圾收集器,叫做 串行收集器,它进行垃圾收集的时候,会停止用户线程(Stop the world)。

新生代垃圾收集器用 Serial,基于复制算法

老年代垃圾收集器用 Serial Old,基于标记整理算法。

优点:所有垃圾收集器中内存消耗最小的,对单核或单线程处理器来说,效率很高。运行在客户端

缺点:stop the world

ParNew收集器

​ ParNew收集器就是Serial收集器的多线程并行版,除了支持多线并行收集之外,没有太多创新之处。

CMS垃圾收集器作为老年代垃圾收集器,不能与Parallel Scavenge配合工作,只能选择 ParNew或者Serial收集器。

Parallel Scavenge收集器

新生代垃圾收集器,基于 标记复制算法,也是可以通过并行收集的多线程收集器。他关注 吞吐量(用户线程时间/总时间)。

CMS 垃圾收集器

CMS 收集器是一种以获取最短停顿时间作为目标的垃圾收集器。基于标记清除算法。

步骤:

初始标记->并发标记->重新标记->并发清除

1)初始标记:stop the world,标记GC ROOT 能直接关联的对象,速度很快

2)并发标记:从GC Root直接关联的对象开始遍历整个对象图,时间较长,但与用户线程并行

3)重新标记:stop the world,修正并发标记期间对象的状态的改变(增量更新算法,标记新的黑色指向白色的引用),时间也比较短。

4)并发清除:与用户线程同步。

优点:并发收集、低停顿。

缺点:

1)对处理器资源敏感,当处理器核心数量在四个以下时,CMS对用户程序影响很大。

2)有浮动垃圾,并发标记时会产生新的垃圾,但是CMS本次不会清理它,要等到下一次才会清理。从而可能造成内存不够而产生Stop the world 的Full GC

3)基于标记清除算法,会产生大量的空间碎片,而触发Full GC

G1垃圾收集器

关注吞吐量和延迟时间的最佳平衡。

从整体看,主要采用标记整理算法,从局部看,是标记-复制算法(两个Region 之间的复制)。

G1把 java对 划分为多个大小相等独立区域 Region,每一个Region 都可以根据需要,扮演 Eden空间、Survivor空间或者老年代空间。

G1对年代的划分存在概念上,它可以不是连续的区间。。

Region 还有一类特殊的Humongous区域,专门存储大对象(1M-32M,可配),把超过Region大小的对象分配在连续的 Humongous Region之中。

每次收集时以Region作为最小单元,G1收集器去根据Region里面垃圾队的价值大小,在后台维护一个优先级列表,优先处理回收价值收益最大的那些Region(每次收集到的内存大小及回收时间的经验值)。

G1至少耗费打印java堆容量的10%到20%来维持收集工作。

TAMS指针:在并发过程中保存新建的对象。

STAB:灰色引用对白色引用的删除 记录下来。

G1的步骤:

1)初始标记:stop the world,仅仅标记GC ROOTs 能直接关联的对象,并且修改TAMS指针

2)并发标记:从GC ROOTS 直接关联的对象出发,扫描对象图(并还要处理SATB记录下并时有变动的对象),时间长,但是与用户线程并行,

3)最终标记:stop the world,短暂,处理遗留下来的少量SATB记录

4)帅选回收:stop the world,负责更新Region的统计数据,对Region的回收价值和成本进行排序。根据用户所期望的停顿时间(JVM参数可配)来制定回收计划。可以自由选择多个Region作为回收集,然后把存活的对象复制到空的Region中,然后清空回收集的Region。

除了并发标记外,其它都用停止用户线程,目标不是单纯的追求低延迟,而是延迟可控的情况下获取最大的吞吐量(用户线程时间/总时间,总时间为用户线程时间+垃圾收集时间)。

以上为个人经验,希望能给大家一个参考,也希望大家多多支持我们。

(0)

相关推荐

  • 简单介绍Java垃圾回收机制

    Java的内存分配与回收全部由JVM垃圾回收进程自动完成.与C语言不同,Java开发者不需要自己编写代码实现垃圾回收.这是Java深受大家欢迎的众多特性之一,能够帮助程序员更好地编写Java程序. 这篇教程是系列第一部分.首先会解释基本的术语,比如JDK.JVM.JRE和HotSpotVM.接着会介绍JVM结构和Java堆内存结构.理解这些基础对于理解后面的垃圾回收知识很重要. Java关键术语 JavaAPI:一系列帮助开发者创建Java应用程序的封装好的库. Java开发工具包(JDK):一

  • 老生常谈Java虚拟机垃圾回收机制(必看篇)

    在Java虚拟机中,对象和数组的内存都是在堆中分配的,垃圾收集器主要回收的内存就是再堆内存中.如果在Java程序运行过程中,动态创建的对象或者数组没有及时得到回收,持续积累,最终堆内存就会被占满,导致OOM. JVM提供了一种垃圾回收机制,简称GC机制.通过GC机制,能够在运行过程中将堆中的垃圾对象不断回收,从而保证程序的正常运行. 垃圾对象的判定 我们都知道,所谓"垃圾"对象,就是指我们在程序的运行过程中不再有用的对象,即不再存活的对象.那么怎么来判断堆中的对象是"垃圾&q

  • Java 垃圾回收机制详解及实例代码

     Java 垃圾回收机制详解 乍一看,垃圾回收所做的事情应当恰如其名--查找并清除垃圾.事实上却恰恰相反.垃圾回收会跟踪所有仍在使用的对象,然后将剩余的对象标记为垃圾.牢记了这点之后,我们再来深入地了解下这个被称为"垃圾回收"的自动化内存回收在JVM中到底是如何实现的. 手动管理内存 在介绍现代版的垃圾回收之前,我们先来简单地回顾下需要手动地显式分配及释放内存的那些日子.如果你忘了去释放内存,那么这块内存就无法重用了.这块内存被占有了却没被使用.这种场景被称之为内存泄露. 下面是用C写

  • JVM的7种垃圾回收器(小结)

    垃圾回收算法和垃圾回收器 对于JVM的垃圾回收算法有复制算法.标记清除.标记整理. 用阳哥的话就是:这些算法只是天上飞的理念,是一种方法论,但是真正的垃圾回收还需要有落地实现,所以垃圾回收器应运而生. JVM回收的区域包括方法区和堆,jvm对于不同区域不同的特点采用分代收集算法,比如因为所有的对象都是在Eden区进行分配,并且大部分对象的存活时间都不长,都是"朝生夕死"的,每次新生代存活的对象都不多,所以新采取复制算法:而jvm默认是新生代的对象熬过15次GC才能进入老年代,所以老年代

  • java 垃圾回收机制以及经典垃圾回收器详解

    判断对象存活方法 引用计数法:在对象中添加一个引用计数子,每当一个地方引用他时,计数器就加一,当引用失效时,计数器就减一. 会有对象循环引用问题: objA.instance = objB objB.instance = objA objA 有objB 的引用 objB 有 objA 的引用,他们相互引用着对方.导致他们无法回收. 可达性分析: 从GC Roots 根对象作为起点,根据引用关系向下搜索,如果对象可达,就说明对象存活,如果对象不可达,就说明对象可以被回收. GC Roots的根对象

  • Java垃圾回收之分代收集算法详解

    概述 这种算法,根据对象的存活周期的不同将内存划分成几块,新生代和老年代,这样就可以根据各个年代的特点采用最适当的收集算法.可以用抓重点的思路来理解这个算法. 新生代对象朝生夕死,对象数量多,只要重点扫描这个区域,那么就可以大大提高垃圾收集的效率.另外老年代对象存储久,无需经常扫描老年代,避免扫描导致的开销. 新生代 在新生代,每次垃圾收集器都发现有大批对象死去,只有少量存活,采用复制算法,只需要付出少量存活对象的复制成本就可以完成收集:可以参看我之前写的Java垃圾回收之复制算法详解 老年代

  • PHP5.3的垃圾回收机制(动态存储分配方案)深入理解

    垃圾回收机制是一种动态存储分配方案.它会自动释放程序不再需要的已分配的内存块. 自动回收内存的过程叫垃圾收集.垃圾回收机制可以让程序员不必过分关心程序内存分配,从而将更多的精力投入到业务逻辑. 在现在的流行各种语言当中,垃圾回收机制是新一代语言所共有的特征,如Python.PHP.Eiffel.C#.Ruby等都使用了垃圾回收机制. 虽然垃圾回收是现在比较流行的做法,但是它的年纪已经不小了.早在20世纪60年代MIT开发的Lisp系统中就已经有了它的身影, 但是由于当时技术条件不成熟,从而使得垃

  • 详解JavaScript的垃圾回收机制

    目录 为什么需要垃圾回收(GC) 什么是垃圾回收 垃圾产生 垃圾回收策略 引用计数标记 循环引用引发的问题 解决方法 引用计数算法的优缺点 标记清除算法 核心思想 标记清除算法优缺点 标记整理算法 V8引擎的垃圾回收 回收新生代对象 对象晋升机制 回收老生代对象 参考文档: 总结 为什么需要垃圾回收(GC) 程序和人一样,生活时间长了会产生垃圾,程序在运行过程中也会产生垃圾,垃圾积攒过多后,会导致程序运行速度变慢. 在JavaScript中的字符串.对象.数组等数据的内存是不固定的,只有真正使用

  • PHP session垃圾回收机制实例分析

    本文实例讲述了PHP session垃圾回收机制.分享给大家供大家参考,具体如下: session过期时间 在php.ini文件中有这样一个配置,这个配置表示session文件过期时间,默认的话是1440秒,也就是24分钟,这个24分钟是session的发呆时间,如果在24分钟内没有对session进行操作,那么session文件就会过期,如果在23分钟的时候操作了session,那么就会又有24分钟的过期时间,如果过期了,该session被服务器认为是垃圾. session.gc_maxlif

  • 基于java中stack与heap的区别,java中的垃圾回收机制的相关介绍

    #. 在java中有两类内存.分别称为stack(堆栈)和heap(堆). stack是程序内存空间,因此所有的基本类型和对象的引用是存在stack中. heap是java虚拟机储存对象的,它是一个巨大的内存,当你创造一个对象,java虚拟机把对象放入heap中,把创造的对象的地址放入stack中. 因此,基本类型.对象的引用储存在stack中:对象储存在heap中. #. java中的垃圾回收机制 当你new一个新的对象,java分配必需的内存.当你用完一个对象时,java的垃圾回收器为你把内

  • 简单理解Java的垃圾回收机制与finalize方法的作用

    垃圾回收器要回收对象的时候,首先要调用这个类的finalize方法(你可以 写程序验证这个结论),一般的纯Java编写的Class不需要重新覆盖这个方法,因为Object已经实现了一个默认的,除非我们要实现特殊的功能(这 里面涉及到很多东西,比如对象空间树等内容). 不过用Java以外的代码编写的Class(比如JNI,C++的new方法分配的内存),垃圾回收器并不能对这些部分进行正确的回收,这时就需要我们覆盖默认的方法来实现对这部分内存的正确释放和回收(比如C++需要delete). 总之,f

  • Java 垃圾回收机制详解(动力节点Java学院整理)

    1. 垃圾回收的意义 在C++中,对象所占的内存在程序结束运行之前一直被占用,在明确释放之前不能分配给其它对象:而在Java中,当没有对象引用指向原先分配给某个对象的内存时,该内存便成为垃圾.JVM的一个系统级线程会自动释放该内存块.垃圾回收意味着程序不再需要的对象是"无用信息",这些信息将被丢弃.当一个对象不再被引用的时候,内存回收它占领的空间,以便空间被后来的新对象使用.事实上,除了释放没用的对象,垃圾回收也可以清除内存记录碎片.由于创建对象和垃圾回收器释放丢弃对象所占的内存空间,

随机推荐