JVM垃圾回收

 

一.对象查找

    在对对象回收之前,需要首先查找出亟待回收的对象,在JVM中,采取"根检索"算法来查找"死亡"的对象;这个算法的基本思想是通过一系列名为"GC Roots"的对象作为起始点,从这些节点开始向下搜索,搜索所经过的路径为对象引用链;如果一个对象到GC Roots上没有任何引用链可达,那么此对象就是"不可达".可以作为GC Roots的对象大概可以分为如下2种:

    1) 方法栈(包括本地方法栈)的本地变量表中的引用的对象.[表明这些对象正在被使用,不能被GC]

    2) 方法区中的类静态属性引用的对象以及常量引用的对象.[这些引用对象,几乎可以认为是不会被回收的,作为Roots非常方便]

    标记一个对象是否可以被回收,不仅和GC Roots的可达性有关(适合强引用),而且还和对象的引用类型有关,例如Soft reference将会在即将OOM时被回收,week reference在每次GC(Full GC?)都将被回收.针对一个对象覆盖了finalize方法,在被回收之前此方法将会被执行,此后对象被标记为"已执行",如果此对象在finalize方法中再次"逃逸"(重新获得对自己的引用),那么此对象很有可能在本次GC后幸存,但是此后如果对象再次被标记为GC,将直接被回收,不会再执行finalize方法(此方法只会被执行一次).

 

    对于方法区,仍然可能被回收,比如"无用的常量池"和"已经卸载的类信息"都会被GC.对于常量池,如果一个常量没有被任何对象所引用,那么它将会被回收;对于类信息比较特殊,因为Class信息可以被JVM预加载也可以在运行期间动态加载,那么类信息回收的条件需要全部具备如下情况:

    1) 如果此类没有任何对象实例存在

    2) 此类的类加载器已经被回收

    3) 此类的Class对象,没有被任何对象引用(特别是反射机制中)

    其实JVM还提供了一个可供参考的参数-Xnoclassgc,此参数用来控制是否对class进行回收;当JVM中存在大量类反射操作/动态代理/自定义类加载器/CGLIB等使用场景时,极有可能会在运行时动态生成大量的代理类,会对方法区的存储空间带来冲击,建议开启此参数(默认开启).

 

二.垃圾回收算法

1) 标记-清除(Mark and sweep):标记"亟待清除"的对象,然后清除;这种算法很简单,不存在内存块的移动,对于被清除的对象所占空间直接被释放(NULL);但是问题也很明显,每次回收之后,内存地址将不再处于连续状态,即出现内存碎片问题,对于大对象内存申请,将可能找不到合适的连续的空间存储,尽管整体上具有较多的空闲内存.

2) 复制算法:必须具备2个独立的内存区域(空间尺寸可以不同),每次回收,都会首先标记存活的对象,然后将这些对象复制到第二个空闲的内存区域中,然后清除先前的内存区域.此算法,完全避免了内存碎片问题,但是它的一个潜在缺点就是需要一个"冗余"的内存空间,这个思想被使用在了JVM新生代的内存模型和GC中.

3) 标记-整理:它和1)相似,但是此算法对对象的整理上,有内存移动(对齐)的操作;此处的"整理"就是让所有的存活的对象"压缩"并对齐,最终保证所有的内存都是连续的,不存在内存碎片.JVM对旧生带的回收会采用1)算法,经过多次Full GC后,旧生带会有大量内存碎片,此时结合3)的整理方式进行“压缩”以减少内存碎片。

三.垃圾回收器


 

     JVM目前存在上述垃圾回收器,根据其所作用的Heap区域或者作用不同,分为2大类.其中SerialNew,ParNew,Parallel Scavenge适用新生代回收(minor GC),CMS/SerialOld/ParallelOld适用于旧生代.真对JVM环境,上述2部分可以有多种组合,比较常见的有:ParNew + CMS + SerialOld(失败担保);Parallel scavenge + Parallel Old;SerialNew + SerialOld[Client模式下];其中前两者适用于Server模式下,后者为client模式下默认组合.另外,jdk7引入了G1收集器,将整个堆分成多个region进行回收。

    1) Serial收集器:单线程收集器,适用于minor GC,在垃圾回收期间,将会阻塞用户线程执行,即"Stop the world",直到收集结束.此收集器为client模式下默认收集器,当机器的CPU为单核或者内存<2G时等将会采用此收集器.采取"复制算法"。

    2) ParNew收集器:多线程并行收集器,它只不过是serial收集器的多线程版,收集效率较高,但是仍然存在"Stop the world"(暂停应用,多线程并行标记“死亡”的对象,单线程复制剩余的存活的对象);可以与CMS收集器配合使用.在多核CPU(>2)环境中,ParNew效率稍高;可以通过-XX:ParallelGCThreads来指定线程的数量."复制算法".

    3) Parallel Scavenge:它和ParNew一样为多线程收集器,也采取了复制算法;此收集器主要是用在"吞吐量优先"的环境中,"吞吐量"即为"JVM实际服务时间"/(JMV服务时间 + GC耗时);此收集器提供了多个控制GC时间的参数,例如:-XX:MaxGCPauseMillis控制GC最大停顿时间,-XX:GCTimeRatio来控制GC耗时比率(默认为99,即GC耗时为1%);在此收集器下,可以使用-XX:UseAdapterSizePolicy让JVM自适配新生代/旧生代的尺寸,以及晋升到旧生代的时机等;此参数可以让JVM帮助我们调优."复制算法".

    4) SerialOld收集器:单线程收集器,适用于旧生代,采用了"标记-整理"算法,此收集器在client模式下使用,最主要的是它作为CMS收集器在"分配担保失败"时,采取的后备方案;

    5) ParallelOld:它和Parallel Scavenge对应,是旧生代的多线程版本.采取"标记-整理"算法.

    6) CMS:Concurrent Mark Sweep,即并发"标记清除";它是一种最小停顿时间为目标的收集器.它采取多线程方式的"标记"对象(会暂停应用程序),在清理阶段是允许用户线程继续运行,这就是并发(注意和"并行"的区别);因为CMS在运行期间,允许用户线程继续进行,也就意味着标记之后,仍会有新的"垃圾对象"生成(即在回收时,仍然需要确保用户对象可以被继续创建);出于此原因,CMS设定了空间占比参数(--XX:CMSInitiatingOccupanFraction,默认为68%),即当旧生代的使用空间达到68%时,就会触发Full GC,以防止收集期间大量对象被创建而造成的空间不足问题.如果不幸预留的空间不足,将会出现一次"Concurrent Mode Failure",进而采取"失败担保"策略,即使用SerialOld重新进行Full GC.

    此外,CMS还有个缺陷就是内存碎片,因为其采用了"标记清除"算法,碎片将难免;这会给大对象的创建带来麻烦,可能旧生代剩余空间足够但是仍无法满足大对象创建时,会额外的进行一次内存碎片整理;-XX:+UseCMSCompactAtFullCollection表示在full gc之后是否进行一次内存整理;-XX:CMSFullGCsBeforeCompaction=<value>可以设定多少次Full GC之后进行一次内存碎片整理.

 

JVM参数配置:

-XX:[+ | -]UseSerialGC:使用Serial + SerialOld组合

-XX:[+ | -]UseParNewGC:使用ParNew + SerialOld组合

-XX:[+ | -]UseConcMarkSweepGC:使用ParNew + CMS + SerialOld(失败担保)组合

-XX:[+ | -]UseParallelGC:使用Parallel scavenge + SerialOld组合

-XX:[+ |-]UseParallelOldGC:使用Parallel scavenge + Parallel Old组合.

 

1) Minor GC:适用于新生代,当新的对象在新生代中无法满足时,将触发一次Minor GC;Minor GC在整个JVM周期中,执行较为频繁.(From或者to两个交换区中alive的那个满时会触发minor GC,如果一个对象过大超过了-XX:PretenureSizeThreshold将会直接在旧生带分配。)

2) Major GC:适用于旧生代,当旧生代使用空间达到阀值或者新生代空间不足时,将会触发Major GC,即为Full GC,触发Major GC之前往往伴随着一次Minor GC.

3) 对象进入旧生代的时机:Full GC将新生代对象迁移到旧生代,迁移的对象可能是"对象年龄"达到设定值(-XX:MaxTenuringThreshold=<value>),也可能是当前新生代中存在大量"长时间存活的对象"(Minor GC保留下来);对于大对象的创建,将会直接分配到旧生代(-XX:PretenureSizeThreshold=<value>,单位byte).

4) 空间分配担保:在发生Minor GC时,虚拟机会检测之前每次晋升到旧生代的平均大小是否大于当前旧生代的剩余空间大小;如果大于,则直接进行一次Full GC;如果小于,则查看-XX:+HandlePromationFailure是否开启,如果开启则继续Minor GC,如果关闭,则改为Full GC;

原文链接:[http://wely.iteye.com/blog/2228827]

 

时间: 2024-10-03 21:47:21

JVM垃圾回收的相关文章

gc-通过Java RMI的定时GC触发,JVM垃圾回收就无法自动触发FULL GC了吗?

问题描述 通过Java RMI的定时GC触发,JVM垃圾回收就无法自动触发FULL GC了吗? Java RMI的定时GC触发机制,可通过:-XX:+DisableExplicitGC来禁止或通过 -Dsun.rmi.dgc.server.gcInterval=3600000来控制触发的时间. 那么问题来了堆栈内存满了,是否就不会自动触发FULL GC了. 目前系统几次宕机,看内存监控均为堆栈满了,但是没能触发FULL GC. 看应用服务启动配置中有Dsun.rmi.dgc.server.gcI

jvm 垃圾回收 分代-JVM垃圾回收关于New Generation为什么分为三个区域的疑问

问题描述 JVM垃圾回收关于New Generation为什么分为三个区域的疑问 最近打算写一个分代垃圾回收,我打算在New Generation中分为两个区域,即只有 from区域 跟 to区域,但是最近查看了好多关于JVM的分代垃圾机制的文章,JVM在New Generation中分为三个区域,即Eden.from.to三个区域,好多文章介绍Eden都是该区域是一整块连续的堆,加速了分配的速度,但是我想问的是,如果只分为两个区域,每次从from拷贝到to后,to区域剩下的空间也是连续的,分配

JVM内存管理、JVM垃圾回收机制、新生代、老年代以及永久代

如果大家想深入的了解JVM,可以读读周志明<深入理解Java虚拟机:JVM高级特性与最佳实践>      需要掌握的东西,包括以下内容.判断对象存活还是死亡的算法(引用计数算法.可达性分析算法).常见的垃圾收集算法(复制算法.分代收集算法等以及这些算法适用于什么代)以及常见的垃圾收集器的特点(这些收集器适用于什么年代的内存收集).            JVM运行时数据区由程序计数器.堆.虚拟机栈.本地方法栈.方法区部分组成,结构图如下所示.      JVM内存结构由程序计数器.堆.栈.本地

JVM垃圾回收描述

垃圾回收分多级,0级为全部(Full)的垃圾回收,会回收OLD段中的垃圾:1级或以上为部分垃圾回收,只会回收Young中的垃圾,内存溢出通常发生于OLD段或Perm段垃圾回收后,仍然无内存空间容纳新的Java对象的情况. 当一个URL被访问时,内存申请过程如下: JVM会试图为相关Java对象在Eden中初始化一块内存区域 当Eden空间足够时,内存申请结束.否则到下一步 JVM试图释放在Eden中所有不活跃的对象(这属于1或更高级的垃圾回收):释放后若Eden空间仍然不足以放入新对象,则试图将

【转载】JVM垃圾回收

本文转载自http://shift-alt-ctrl.iteye.com/blog/1842714   一.对象查找     在对对象回收之前,需要首先查找出亟待回收的对象,在JVM中,采取"根检索"算法来查找"死亡"的对象;这个算法的基本思想是通过一系列名为"GC Roots"的对象作为起始点,从这些节点开始向下搜索,搜索所经过的路径为对象引用链;如果一个对象到GC Roots上没有任何引用链可达,那么此对象就是"不可达".

Java虚拟机的JVM垃圾回收机制

1.JVM内存空间     JVM堆(Heap)= 新生代(Young) + 旧生代(Tenured)  分区作用:  新创建的对象通常先将其分配在新生代中,在新生代中经过若干次GC之后仍未释放的对象,再将它移动到旧生代.为了让内存回收更高效(GC会暂停JVM中的应用),Sun JDK在1.2开始对堆采用了分代管理的方式.在分配对象遇到内存不足时,先对新生代进行GC(Young GC):当新生代GC之后仍无法满足内存空间分配需求时, 才会对整个堆空间以及方法区进行GC (Full GC) 

jvm 内存与垃圾回收

你对JVM内存组成结构和JVM垃圾回收机制是否熟悉,这里和大家简单分享一下,希望对你的学习有所帮助,首先来看一下JVM内存结构,它是由堆.栈.本地方法栈.方法区等部分组成,结构图如下所示. JVM学习笔记 JVM内存管理和JVM垃圾回收 JVM内存组成结构 JVM内存结构由堆.栈.本地方法栈.方法区等部分组成,结构图如下所示: 1)堆 所有通过new创建的对象的内存都在堆中分配,其大小可以通过-Xmx和-Xms来控制.堆被划分为新生代和旧生代,新生代又被进一步划分为Eden和Survivor区,

从JVM的内存管理角度分析Java的GC垃圾回收机制_java

一个优秀的Java程序员必须了解GC的工作原理.如何优化GC的性能.如何与GC进行有限的交互,因为有一些应用程序对性能要求较高,例如嵌入式系统.实时系统等,只有全面提升内存的管理效率 ,才能提高整个应用程序的性能.本篇文章首先简单介绍GC的工作原理之后,然后再对GC的几个关键问题进行深入探讨,最后提出一些Java程序设计建议,从GC角度提高Java程序的性能.    GC的基本原理    Java的内存管理实际上就是对象的管理,其中包括对象的分配和释放.     对于程序员来说,分配对象使用ne

JVM分代垃圾回收策略的基础概念

由于不同对象的生命周期不一样,因此在JVM的垃圾回收策略中有分代这一策略.本文介绍了分代策略的目标,如何分代,以及垃圾回收的触发因素. 文章总结了JVM垃圾回收策略为什么要分代,如何分代,以及垃圾回收的触发因素. 为什么要分代 分代的垃圾回收策略,是基于这样一个事实:不同的对象的生命周期是不一样的.因此,不同生命周期的对象可以采取不同的收集方式,以便提高回收效率. 在Java程序运行的过程中,会产生大量的对象,其中有些对象是与业务信息相关,比如Http请求中的Session对象.线程.Socke