2019独角兽企业重金招聘Python工程师标准>>>

JVM进行垃圾回收时要考虑哪的问题如下:

1.如何判定对象为垃圾对象?

  1.引用计数法:在对象中添加一个引用计数器,当有地方引用这个对象的时候,引用计数器的值就+1,引用失效的时候,计数器的值就-1,

  直到计数器的值为0时,就被垃圾回收器回收。这种方式实现简单,判定效率也是比较高的,单是但遇到一种情况就不行了,比如说堆中的对象实例相互引用,断开被栈引用。这样由于

  堆中实例对象相互引用,而引用计数器的值却不会不会变成0。这种方式导致无法回收。目前为止,java的回收器基本没有使用这种算法。我们用代码看一下,到底有没有用这种算法。如下代码:

  

package hjc.test7;public class Main {private Object instance;public Main() {byte [] m = new byte[20*1024*1024];}public static void main(String[] args) {Main m1 = new Main();Main m2 = new Main();m1.instance = m2;m2.instance = m1;m1 = null;m2 = null;System.gc();}
}

  在虚拟机配置如下参数,如图所示:

  

  

  结果如下图:

可以看到堆中的对象实例相互引用,断开被栈引用的这部分确实被回收了,这也证明了java回收器中没用这种算法。

  2.可达性分析法。

    这个算法的基本思路就是通过一系列的称为"GC Roots"的对象作为起始点,从这些节点开始向下搜索,搜索所走过的路径成为引用链,当一个对象到GC Roots没有任何引用链相连(用数据结构的图论来说,就是从GC Roots到达这个对象不可达)时,

    则证明此对象是不可用的。如下图所示:对象object5,6,7虽然互相有关联,但是他们到GC Roots是不可达的,所以他们将会被判断为是可回收的对象。

      

    在JAVA语言中,可作为GC Roots的对象包括下面几种:

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

      2.方法区中类静态属性引用的对象。

 

  这里还要说一些细节:

    无论是通过引用计数算法判断对象的引用数量,还是通过可达性分析算法判断对象的引用链是否可达,断定对象是否存活都与“引用”有关。在JDK1.2以前,JAVA的引用定义很传统:如果reference类型的数据中存储的数值代表的是另外一个块内存的起始地址,

    这块内存代表着一个引用。这种定义很存粹,太过狭隘,一个对象在这种定义下只有被引用或者没有被引用两种状态,对于如何描述一些“食之无味,弃之可惜”的对象就显得无能为力。因此我们希望描述这样一类对象:当内存空间还足够时,能够保留在内存中;

    如果内存空间在垃圾收集后还是非常紧张,则可以抛弃这些对象。

    Java在JDK1.2对引用的概念进行了扩充,将引用分为强引用,软引用,弱引用,虚引用。

    

    强引用:指在程序代码之中普片存在的,类似“Object obj = new Object()”这类的引用,只要强引用还存在,垃圾收集齐永远不会回收被引用的对象。

    软引用:用来描述一些还有用但并非必须的对象。对于软引用关联着的对象,在系统将要发生内存溢出异常之前,会把这些对象列进回收范围之中进行第二次回收。如果这次回收还没有足够的内存,才会抛出内存溢出异常。

    在JDK1.2之后,提供了SoftReference类来实现软引用。

    弱引用:也是用来描述非必需对象的,但是它的强度比软引用更弱一些,被弱引用关联的对象只能生存到下一次垃圾收集发生之前。当垃圾收集齐工作时,无论当前内存是否足够,都会回收掉被弱引用关联的对象。在JDK1.2之后,提供了

    WeakReference类来实现弱引用。

    虚引用:称为幽灵引用或者幻影引用,它是最弱的一种引用关系。一个对象是否有虚引用的存在,完全不会对其生存时间构成影响,也无法通过虚引用来取得一个对象实例。为一个对象设计虚引用关联的唯一目的就是能在这个对象被收集器回收时

    收到一个系统通知。提供了PhantomRefernce类来实现虚引用。

    判定一个类是否为"无用的类"必须同时满足3个条件才行。

      1.该类所有的实例都已经被回收,也就是Java堆中不存在该类的任何实例。

      2.加载该类的ClassLoader已经被回收了。

      3.该类的对应的java.lang.Class对象没有在任何地方被引用,无法在任何地方通过反射访问该类的方法。

    虚拟机满足上面3个条件的无用类进行回收,注意,仅仅是”可以“,而并不是和对象一样,不使用了就必然会回收。HostSpot虚拟机提供了 -Xnoclassgc 参数进行控制,还可以使用-verbose:class以及-XX:TraceClassLoading,-XX:+TraceClassUnLoading查看类

    加载和卸载信息,其中-verbose:class和-XX:TraceClassLoading可以在Product版的虚拟机中使用,-XX:TraceClassUnLoading参数需要FastDebug版的虚拟机支持。

    在大量使用反射,动态代理,CGLib等ByteCode框架,动态生成JSP以及OSGi这类频繁自动义ClassLoader的场景都需要虚拟机具备类卸载的功能,以保证永久代不会溢出。

2.如何回收?

  1.回收策略。

    1.标记-清除算法

      算法分为”标记“和”清楚“两耳阶段:首先标记出所有需要回收的对象,在标记完成后统一回收所有被标记的对象,它的标记过程在前面的笔记已经出现过了。它是最基础的收集算法,因为后面的算法都是基于这种思路并对其不足进行改进而得到的。

      它主要有两个不足:一个是效率问题,标记和清除两个过程的效率都不高;另外一个是空间问题,标记清除后会产生大量不连续的内存碎片,空间碎片太多可能会导致以后在程序运行过程中需要分配较大对象时,无法找到足够的连续内存而不得不提前

      触发另外一次垃圾收集动作。标记-清楚算法执行过程如下图:

      

      

    2.复制算法。

      为了解决效率问题,一种称为”复制“的收集算法出现了,它将可用内存按容量划分为大小相等的两块,每次只使用其中的一块。当这一块的内存用完了,就将还存活着的对象复制到另外一块上面,然后再把已使用过的内存空间一次清理掉。

      这样使得每次都是对整个半区进行内存回收,内存分配时,也就不用考虑内存碎片等复杂情况,只要移动堆指针,按顺序分配内存即可,实现简单,运行高效。只是这种算法的代价是将内存缩小为了原来的一半,未免太高了一点。

      算法执行过程如下图:

      

      IBM公司的专门研究表明,新生代中的对象98%是”朝生夕死“的,所以并不需要按照1:1的比例来划分内存空间,而是将内存分为一块较大的Eden空间和两块较小的Survivor空间,每次使用Eden和其中一个块Survivor。

      当回收时,将Eden和Survivor中还存活着的对象一次性的复制到另外一块Survivor空间上,最后清理掉Eden和刚才用过的Survivor空间。HostSpot虚拟机默认Eden和Survivor的大小比例是8:1,也就是说每次新生代中可用

      内存空间为真个新生代容量的90%,只有10%内存会被浪费。当另外一片Survior空间不够用时,需要依赖其他内存(老年代)进行分配担保,因此另外一片Survivor空间没有足够空间存放上一次新生代收集下来的存活对象时,这些对象将直接通过分配担保机制进入老年代。      

    3.标记-整理算法

      复制手机算法在对象存活率较高时就要进行较多的复制操作,效率将会变低,更关键的是,如果不想浪费50%的空间,就需要有额外的空间进行非配担保,以应对被使用的内存中所有对象都100%存活的极端情况,所以在老年代一般不能直接选用这种算法。

      标记-整理算法仍然与标记-清除算法一样,但后续步骤不是直接对可回收对象进行清理,而是让所有存活对象都向一端移动,然后直接清理掉端边界以外的内存,标记-整理算法,如下图所示:

      

    4.分代收集算法

     这种算法思想是根据对象存活周期的不同将内存划分为几块,一般是把Java堆分为新生代和老年代,这样就可以根据各个年代的特点采用最合适的收集算法。在新生代中,每次垃圾收集时都发现有大批对象死去,只有少量存活,那就算用赋值算法。

      只需要付出少量存活对象的复制成本就可以完成收集。而老年代总因为对象存活率搞,没有额外空间对他进行分配担保,就必须使用"标记-清理"或者”标记整理“算法来回收。

  2.垃圾回收器

    1.Serial

      Serial曾经是在JDK1.3之前是虚拟机新生代收集的唯一选择,是一个单线程的收集器,但它的单线程的意义并不仅仅说明它只会使用一个CPU或一条收集线程去完成垃圾收集工作,更重要的是它在运行垃圾收集时,必须暂停其他所有的工作线程,

      直到它收集结束。从JDK1.3到现在最新的1.9,HostSpot虚拟机团队为消除或者减少工作线程因内存回收而导致停顿的努力一直在进行着从Serial到Parallel收集器,再从Concurrent Mark Sweep(CMS)到GC收集器最前沿成果Garbage First(G1)收集器,

      用户线程的停顿时间在不断缩短,但是仍然没办法完全消除(这里暂不包括RTSJ中的收集器)。到现在为止,它依然是虚拟机运行在Client模式下的默认新生代收集器。它的优点在于:简单而高效(与其他收集器的单线程比),对于限定单个CPU的环境来说

      Serial收集器由于没有线程交互的开销,专心做垃圾收集自然可以获得最高的单线程收集效率。在用户的桌面应用场景中,分配给虚拟机管理的内存一般来说不会很大,收集几十兆到一两百兆的新生代(仅仅是新生代使用的内存),停顿时间是完全可以控制

      在即时毫秒最多一百多毫秒以内,只要不要频繁发生,这停顿是可以接受的。所以Serial对于运行在Client模式下的虚拟机来说是一个很好的选择。如下图:

      

    2.Parnew

      Parnew收集器其实就是Serial收集器的多线程版本,除了使用多线程进行垃圾收集之外,其余行为包括Serial收集器可用的所有控制参数(例如:-XX:SurvivorRatio,-XX:PretenureSizeThreshold,-XX:HandlePromotionFailure等),收集算法,Stop the World,对象分配规则

      ,回收策略等都与Serial收集器完全一样,共用了相当多的代码。其工作过程如下图:

      

      Parnew收集器却是许多运行在Server模式下的虚拟机中首选的新生代收集器,其中有一个与性能无关但很重要的原因是,除了Serial收集齐外,目前只有它能与CMS收集器配合工作。

      注意:Parnew收集器在单CPU的环境中绝对不会有比Serial收集器更好的效果,甚至由于存在线程交互的开销,该收集器在通过超线程技术实现的两个CPU的环境中都不能百分之百地保证超过Serial收集器。

      对于使用的CPU的数量的增加,它对于GC时系统资源的有效利用还是很有好处的。它默认安康i其的手机线程数与CPU的数量相同,在CPU非常多的环境下,可以使用-XX:ParalleLGCThreads参数来限制垃圾收集器的线程数。

    3.Parallel Scaveng

      这里先说一下垃圾收集器环境下什么是并行,什么是并发。

      并行:指多条垃圾收集线程并行工作,但此时用户线程仍然处于等待状态。

      并发:指用户线程与垃圾收集线程同时执行(但并不一定并行,可能会交替执行),用户程序在继续运行,而且垃圾收集程序运行于另外一个CPU上。

      Parallel Scaveng收集器是一个新生代收集器,它也是使用复制算法的收集器,又是并行的多线程收集器,它的关注点与其他收集器不同,CMS等收集器的关注点是尽可能地缩短垃圾收集时用户线程的停顿时间,

      而Parallel Scaveng收集器的目标则是达到一个可控制的吞吐量。所谓的吞吐量就是CPU用于运行用户代码的时间与CPU总消耗时间的比值,即吞吐量=运行用户代码/(用户代码时间+垃圾收集时间)。虚拟机总共运行100分钟,其中垃圾收集花掉1分钟,

      那吞吐量就是99%。停顿时间越短越适合与用户交互的程序,良好的响应速度能提升用户体验,而高吞吐量可以高效率地利用CPU时间,尽快完成程序的运算任务。

      Parallel Scaveng提供了两个参数用户精确控制吞吐量,分别是控制最大垃圾收集停顿时间的-XX:MaxGCPauseMilis参数以及直接设置吞吐量大小的-XX:GCTimeRatio参数。

      注意:MaxGCPauseMilis参数允许的值是一个大于0的毫秒数,收集器将尽可能地保证内存回收花费的时间不超设置值。不要认为这个参数的值设置越小就能使垃圾收集速度变得更快,GC停顿时间短是以牺牲吞吐量和新生代空间换取的;

      GCTimeRatio参数的值应该是一个大于0且小于100的整数,也就是垃圾收集时间占总时间的比率,相当于是吞吐量的倒数。如果把此参数设置为19,那允许额最大GC时间就占总时间的5%(即1/(1+19)),默认值为99,就是允许最大1%(即1/(1+99))的垃圾收集时间。

      由于与吞吐量关系密切,它通常被称为“吞吐量优先”收集器。Parallel Scavenge收集器还有一个参数-XX:UseAdaptiveSizePolicy值得关注,这是一个开关参数,当这个参数打开后,就不需要手工指定新生代的大小(-Xmm),Eden与Survivor区的比例(-XX;SurvivorRatio)

      晋升老年代对象年龄(-XX:PretenureSizehold)等参数细节,虚拟机会根据当前系统运行情况收集性能监控信息,动态调整这些参数以提供最适合的停顿时间或者最大的吞吐量,这种方式称为GC自适应调节策略。

    4.Serial Old

      Serial Old是Serial收集器的老年代版本,它同样是一个单线程收集器,使用"标记-整理算法",这个收集器的主要意义也是在于给Client模式下的虚拟机使用。如果在Server模式下,那么它主要有两大用途:一种用途就是在JDK1.5以及之前的版本中与Parallel Scavenge收集器搭配使用,

      另外一种用途就是作为CMS收集器的后备预案,在并发收集发生Concurrent Mode Failure时使用。工作流程如下:

      

    5.Parallel Old

      Parallel Old是Parallel Scavenge收集器的老年代版本,使用多线程和“标记-整理”算法。这个收集器是在JDK1.6中才开始提供的,在此之前,新生代的Parallel Scavenge收集器一致处于比较尴尬额状态。原因是,如果新生代选择了Parallel Scavenge收集器,老年代

      除了Serial Old收集器外别无选择。由于老年代Serial Old收集器在服务端应用性能上拖累,使用了Parallel Scavenge收集器也未必能在整体应用上获得吞吐量最大化的效果。由于单线程的老年代收集中无法充分利用服务器多CPU的处理能力,在老年代很大而且硬件

      比较高级的环境中,这种组合的吞吐量甚至还不一定有ParNew+CM组合给力。直到Paralllel Old 收集器出现后,“吞吐量优先”收集器终于有了比较名副其实的应用组合,在注重吞吐量以及CPU资源敏感的场合,都可以有限考虑Parallel Scavenge+Parallel Old收集器。

      它的工作流程图如下:

      

    6.Cms

      CMS收集器是一种以获得最短停顿时间为目标的收集器。目前很大一部分的Java应用集中在互联网站或者B/S系统的服务端上,这类应用尤其重视服务的响应速度,希望系统停顿时间最短,已给用户带来较好的体验。

      CMS收集器是基于“标记-清除算法”实现的,,整个过程分为4个步骤:

        1.初始标记

        2.并发标记

        3.重新标记

        4.并发清除

      注意:初始标记,重标记这两个不走仍然需要“Stop The World”。初始标记仅仅是标记一下GC Roots能直接关联到的对象,毒素很快,并发标记阶段就是GC Roots Tracing的过程,而重新标记则是为了修正并发标记期间因用户程序继续运作而导致

      标记产生变动的那一部分对象的标记记录,这个阶段的停顿时间一般会比初始标记阶段稍长一些,但远比并发标记的时间短。

      由于整个过程中耗时最长的并发标记和并发消除过程收集器线程都可以与用户线程一起工作,所以,总体上来说,CMS收集器的内存回收过程是与用户线程一起并发执行的。

      下面通过CMS收集器看到运作步骤中并发和需要停顿的时间:

        

      CMS有三个明显的缺点:

        1.CMS收集器对CPU资源非常敏感,在并发阶段,他虽然不会导致用户线程停顿,但是会因为占用一部分线程(或者说CPU资源)而导致应用程序变慢,总吞吐量会降低。

        2.CMS收集器无法处理浮动垃圾,可能出现"Concurrent Mode Failure"失败而导致另外一次Full GC的产生。由于CMS并发清理阶段用户线程还在运行着,伴随程序运行自然就还会有新的垃圾不断产生,这一部分垃圾出现在标记过程之后,

        CMS无法在当次收集中处理掉它们,只好留待下一次GC时再清理掉。这一部分垃圾就成为浮动垃圾。由于在垃圾收集阶段用户线程还需要运行,那也就还需要预留有足够的内存空间给用户线程使用,因此CMS不能像其他收集器那样

        等到老年代几乎完全被填满了再进行收集,需要预留一部分空间提供并发收集时的程序运作使用。在JDK1.5的默认设置下,CMS收集器当老年代使用了68%的空间后就会被激活,如果在应用中老年代增长不是很快,可以调高参数

        -XX:CMSinitiatingOccupancyFraction的值来提高触发百分比,以便降低内存回收次数来获得更好的性能,在JDK1.6,启动阈值已经提升至92%。注意,-XX:CMSinitiatingOccupancyFraction的值设置太高,容易导致大量的"Concurrent Mode Failure"失败,性能反而下降。

        3.由于是基于“标记-清除”算法实现的,这意味着收集结束时会有大量空间碎片产生。空间碎片过多时,将会给大对象分配带来很大麻烦,往往导致会出现老年代还有很大空间剩余,但是无法找到足够打的连续空间来分配当前对象。不得不提前出发一次Full GC。

        为了解决这问题,CMS收集器提供一个-XX:+UseCMSCompactAtFullCollection开关参数(默认是开启的),用于CMS收集器顶不住要进行FullGC时开启内存碎片的合并整理过程,内存整理的过程是无法并发的,空间碎片问题也就没有了,但停顿时间不得不变长了。

        虚拟机还提供了另外一个参数-XX:CMSFullGCsBeforeCompaction,这个参数是用于设置执行多少次不压缩的Full GC后,跟着来一次带压缩的(默认为0,表示每次进入FullGC时,都进行碎片整理)。

    7.G1

      G1是一款面向服务端应用的垃圾收集器。与其他GC收集器相比,G1具备如下特点:

        1.并发与并行:G1能充分利用多CPU,多核环境下的硬件优势,使用多个CPU来缩短Stop the World 听段时间。部分其他垃圾收集器原本需要停顿JAVA线程执行的GC的动作,G1收集器仍然可以通过并发的方式让Java程序继续执行。

>>>>阅读全文

转载于:https://my.oschina.net/u/3889140/blog/1834066

JVM笔记6-垃圾回收器相关推荐

  1. JVM笔记:垃圾回收器总结

    垃圾收集器没有在规范中进行过多的规定,可以由不同的厂商.不同版本的JVM来实现. 由于JDK的版本处于高速迭代过程中,因此Java发展至今已经衍生了众多的GC版本.从不同角度分析垃圾收集器,可以将GC ...

  2. JVM常见的垃圾回收器

    一.常见垃圾收集器 现在常见的垃圾收集器有如下几种: 新生代收集器: Serial ParNew Parallel Scavenge 老年代收集器: Serial Old CMS Parallel O ...

  3. JVM专题之垃圾回收器

    一 如何寻找垃圾对象 1.1 引用计数法 每一个对象有一个计数器,当被别的对象引用的时候,则会递增计数器:当别人释放或者引用失效的时候则递减计数器,如果在垃圾回收的时候,引用计数器为0,则表示这个对象 ...

  4. 【JVM】G1垃圾回收器

    1.概述 G1(Garbage First)垃圾收集器是当今垃圾回收技术最前沿的成果之一.早在JDK7就已加入JVM的收集器大家庭中,成为HotSpot重点发展的垃圾回收技术.同优秀的CMS垃圾回收器 ...

  5. 【JVM · GC】垃圾回收器

    1. GC分类与性能指标 1.1 垃圾回收期器概述 垃圾收集器没有在规范中进行过多的规定,可以由不同的厂商.不同版本的JVM来实现. 由于 JDK 版本处于高速迭代过程中,因此Java 发展至今已经衍 ...

  6. JVM中的垃圾回收器详解

    垃圾回收器的种类和使用 1.概述 1.1垃圾回收器和垃圾回收算法的关系 1.2垃圾回收器的分类 1.3 如何使用参数查看当前使用的垃圾回收器 2 串行收集(Serial) 2.1 概念 3 并行收集( ...

  7. JVM七种垃圾回收器

    一 七种垃圾回收器总结: 垃圾收集器 分类 作用位置 使用算法 特点 适用场景 Serial 串行运行 作用于新生代 复制算法 响应速度优先 适用于单CPU环境下的client模式 ParNew 并行 ...

  8. JVM中的垃圾回收器

    文章目录 概述 垃圾回收器分类 GC的性能指标 HotSpot虚拟机中的垃圾收集器 Serial 垃圾收集器(单线程) Serial Old 垃圾收集器(单线程) ParNew 垃圾收集器 Paral ...

  9. JVM系列之垃圾回收器(下篇)——Shenandoah垃圾回收器

    1. 前言 虽然目前大部分系统使用的是 JDK8,使用的垃圾回收器也大概率为 G1 或者更古老的垃圾回收器,但是截止到目前为止,JDK 已经更新到 JDK14 了,垃圾回收器也几乎在每一次迭代中被更新 ...

最新文章

  1. PyTorch 笔记(02)— 常用创建 Tensor 方法(torch.Tensor、ones、zeros、eye、arange、linspace、rand、randn、new)
  2. SharePoint 2013 配置开发环境,需安装VS2012插件
  3. C#中string与byte[]的转换帮助类
  4. Build OpenVSwitch and OVN
  5. MFC中CSliderCtrl的使用(收集)
  6. github上传本地代码
  7. CodeForces - 1059D(二分+误差)
  8. AKS开讲啦! | DevOps with AKS
  9. webpack系列-plugin
  10. 计算机图形学E4——OpenGL 区域种子填充
  11. ES6躬行记(3)——解构
  12. [2019杭电多校第三场][hdu6609]Find the answer(线段树)
  13. 整理了100个必备的 Python 函数,值得收藏
  14. Cisco CCNP课程
  15. 洛谷Java入门级代码之分汽水
  16. 管理学-“三个和尚”
  17. 著名的NP问题:TSP问题(Traveling Salesman Problem)
  18. git基本命令与git基本命令-远程
  19. 网络测试仪哪个牌子好
  20. ACP敏捷认证考试及格线以及考试知识占比分析

热门文章

  1. HDU2255 奔小康赚大钱(km模板题)
  2. linux id命令用来查看账户的uid和gid及所属分组及用户名
  3. using的几种用法 C#
  4. 小米AIoT开发者大会28日召开,雷军的AIoT和小米的这一年
  5. 开源:Angularjs示例--Sonar中项目使用语言分布图(CoffeeScript版)
  6. HDU 3157 Crazy Circuits(有源汇上下界最小流)
  7. sybase备份问题
  8. ORACLE 日期比较
  9. Lync Server 2010标准版系列PART4:部署准备
  10. 网络共享及排错+卷影副本