欢迎访问悦橙教程(wld5.com),关注java教程。悦橙教程  java问答|  每日更新
页面导航 : > > 文章正文

java基础-java垃圾收集机制,

来源: javaer 分享于  点击 23917 次 点评:273

java基础-java垃圾收集机制,


java 垃圾收集机制

本篇从垃圾收集的算法(方法论)和垃圾收集器(方法论的实现)角度讲java 的垃圾收集机制,希望看完本篇文章对java的垃圾收集有较全面的认识。

  概述:java 垃圾收集通过可达性分析算法与两次标记判断对方是否存活,将堆内存分为年轻代(young 1/3)、老年代(old/Tenured 2/3 ), 方法区称为永久代(java7及之前, java 8使用元数据空间(Metaspace) 代替了 PermGen space), 年轻代的内存区域被进一步划分成3个区域Eden、From Survivor、To Survivor 默认比例Eden : from : to = 8 : 1 : 1Eden是进行内存分配的地方,是一块连续的空闲内存区域。两个Survivor中始终有一个是空白的。在进行垃圾回收的时候,Eden和其中一个非空存活区中还存活的对象根据其存活时间被复制到当前空白的存活区或年老世代中,将这些对象的年龄设置为1对象在 Survivor 区每熬过一次 Minor GC,就将对象的年龄 + 1,当对象的年龄达到某个值时 ( 默认是 15 岁,可以通过参数 -XX:MaxTenuringThreshold 来设定 ),这些对象就会成为老年代。一些较大的对象 ( 即需要分配一块较大的连续内存空间 ) 则是直接进入到老年代。GC 分为两种:Minor GC、Full GC ( 或称为 Major GC )。Minor GC 是发生在新生代中的垃圾收集动作,所采用的是复制算法.Full GC 是发生在老年代的垃圾收集动作,所采用的是标记-清除算法。

        下图是整个文章思路的思维导图


一、堆空间图示

下图是jvm 内存模型的示意图, 线程私有的部分



二、对象存活判定算法

         基于引用计数与基于可达性分析这两大算法的自动内存管理方式最大的不同之处在于:前者只需要局部信息,而后者需要全局信息。而java 中对于对象的存活判断算法使用后者, 前者在微软的COM 组件中使用,C++中较为常见。

1.1 引用计数算法

     在引用计数中,每一个对象负责维护对象所有引用的计数值。当一个新的引用指向对象时,引用计数器就递增,当去掉一个引用时,引用计数就递减。当引用计数到零时,该对象就将释放占有的资源。当A引用B ,B引用A 时就会造成循环引用,导致资源一直被占用, 而C++ 引入weak_ptr弱引用指针来解决循环引用问题。


1.2 可达性分析算法       GC Roots利用图论中的可达性原理来判断,内存是否需要被回收. 图中可达对象便是存活对象,而不可达对象则是需要回收的垃圾内存。可达性分析算法需要全局的对象图信息,从对象图的“根”(也就是必然活的引用)出发扫描出去,基于引用的可到达性来判断对象的生死。这使得对象的生死状态只能批量的被识别出来,然后批量释放死对象。


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

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

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

3) 方法区中常量引用的对象

4) 本地方法栈中JNI(即一般说的Native方法)引用的对象

reference1、reference2、reference3都是GC Roots




三、跟踪收集算法


  在确定了哪些垃圾可以被回收后,垃圾收集器要做的事情就是开始进行垃圾回收,但是这里面涉及到一个问题是:如何高效地进行垃圾回收。由于Java虚拟机规范并没有对如何实现垃圾收集器做出明确的规定,因此各个厂商的虚拟机可以采用不同的方式来实现垃圾收集器,所以在此只讨论几种常见的垃圾收集算法的核心思想。

3.1 标记-清除(Mark-Sweep)算法

这是最基础的垃圾回收算法,之所以说它是最基础的是因为它最容易实现,思想也是最简单的。标记-清除算法分为两个阶段:标记阶段和清除阶段。标记阶段的任务是标记出所有需要被回收的对象,清除阶段就是回收被标记的对象所占用的空间。具体过程如下图所示:


 从图中可以很容易看出标记-清除算法实现起来比较容易,但是有一个比较严重的问题就是容易产生内存碎片,碎片太多可能会导致后续过程中需要为大对象分配空间时无法找到足够的空间而提前触发新的一次垃圾收集动作。

3.2 复制(Copying)算法

为了解决Mark-Sweep算法的缺陷,Copying算法就被提了出来。它将可用内存按容量划分为大小相等的两块,每次只使用其中的一块。当这一块的内存用完了,就将还存活着的对象复制到另外一块上面,然后再把已使用的内存空间一次清理掉,这样一来就不容易出现内存碎片的问题。具体过程如下图所示:



 这种算法虽然实现简单,运行高效且不容易产生内存碎片,但是却对内存空间的使用做出了高昂的代价,因为能够使用的内存缩减到原来的一半。

 很显然,Copying算法的效率跟存活对象的数目多少有很大的关系,如果存活对象很多,那么Copying算法的效率将会大大降低。


3.3 标记-整理(Mark-Sweep)算法

为了解决Copying算法的缺陷,充分利用内存空间,提出了Mark-Compact算法。该算法标记阶段和Mark-Sweep一样,但是在完成标记之后,它不是直接清理可回收对象,而是将存活对象都向一端移动,然后清理掉端边界以外的内存。具体过程如下图所示:



3.4  分代收集算法 (Generational Collecting)

   虚拟机中的共划分为三个代:年轻代(Young Generation 堆)、年老代(Old Generation 堆)和持久代(Permanent Generation 方法区、常量池、即时编译代码)。其中持久代主要存放的是Java类的类信息,与垃圾收集要收集的Java对象关系不大。年轻代和年老代的划分是对垃圾收集影响比较大的。

    在新生代中,每次垃圾收集时都发现有大批对象死去,只有少量存活,选用:复制算法, 只需要付出少量存活对象的复制成本就可以完成回收。     在老年代中,因为对象存活率高、没有额外空间对它进行分配担保,就必须使用“标记-清除”或者“标记-整理”算法来进行回收。
                                                


1).新生代:目前大部分垃圾收集器对新生代都采用复制算法,新生代中98%的对象是朝生夕死的短生命周期对象,所以不需要将新生代划分为容量大小相等的两部分内存,而是将新生代分为Eden区,Survivor from和Survivor to三部分,其占新生代内存容量默认比例分别为8:1:1,其中Survivor from和Survivor to总有一个区域是空白,只有Eden和其中一个Survivor总共90%的新生代容量用于为新创建的对象分配内存,只有10%的Survivor内存浪费,当新生代内存空间不足需要进行垃圾回收时,仍然存活的对象被复制到空白的Survivor内存区域中,Eden和非空白的Survivor进行标记-清理回收,两个Survivor区域是轮换的。 默认的,Edem : from : to = 8 :1 : 1 可以通过参数–XX:SurvivorRatio 来设定 ),即: Eden = 8/10 的新生代空间大小,from = to = 1/10 的新生代空间大小。新生代中98%情况下空白Survivor都可以存放垃圾回收时仍然存活的对象,2%的极端情况下,如果空白Survivor空间无法存放下仍然存活的对象时,使用内存分配担保机制,直接将新生代依然存活的对象复制到年老代内存中,同时对于创建大对象时,如果新生代中无足够的连续内存时,也直接在年老代中分配内存空间。

Java虚拟机对新生代的垃圾回收称为Minor GC,次数比较频繁,每次回收时间也比较短。

使用java虚拟机-Xmn参数可以指定新生代内存大小。


    2).老年代:年老代中的对象一般都是长生命周期对象,对象的存活率比较高,因此在年老代中使用标记-整理垃圾回收算法。

Java虚拟机对年老代的垃圾回收称为MajorGC/Full GC,次数相对比较少,每次回收的时间也比较长。

当新生代中无足够空间为对象创建分配内存,年老代中内存回收也无法回收到足够的内存空间,并且新生代和年老代空间无法在扩展时,堆就会产生OutOfMemoryError异常。java虚拟机-Xms参数可以指定最小内存大小,-Xmx参数可以指定最大内存大小,这两个参数分别减去Xmn参数指定的新生代内存大小,可以计算出年老代最小和最大内存容量。

3).永久代: 它用来存储class类、常量、方法描述等。对永久代的回收主要回收两部分内容:废弃常量和无用的类。



三.典型的垃圾收集器

四、垃圾收集器

4.1 Serial 收集器

       串行收集器是最古老,最稳定以及效率高的收集器,可能会产生较长的停顿,只使用一个线程去回收。新生代、老年代使用串行回收;新生代复制算法、老年代标记-压缩;垃圾收集的过程中会Stop The World(服务暂停)

参数控制:-XX:+UseSerialGC  串行收集器



4.2 ParNew收集器
4.3Parallel Scavenge 收集器
4.4Serial Old 收集器
4.5 Parallel Old 收集器
4.6 CMS 收集器
4.7 G1 收集器
目前所有的新生代gc都是需要STW的

Serial:单线程STW,复制算法
ParNew:多线程并行STW,复制算法
Parallel Scavange:多线程并行STW,吞吐量优先,复制算法
G1:多线程并发,可以精确控制STW时间,整理算法

五、垃圾收集参数配置


六、总结


推荐阅读:

http://www.cnblogs.com/dolphin0520/p/3783345.html

http://www.infoq.com/cn/articles/cf-java-garbage-references

相关文章

    暂无相关文章
相关栏目:

用户点评