java中的垃圾收集器是怎么工作的

来源:互联网 发布:象棋打谱软件 编辑:程序博客网 时间:2024/05/22 23:58


        英文原址:点击打开链接

 我看过很多关于Java中垃圾收集器的文章,但其中一些要么理解起来太复杂,要么并没有包含理解java垃圾收集器的足够信息。所以我决定将我对于什么是垃圾收集器和垃圾收集器如何工作方面的经历用简单的语言写成一篇文章或者你说的教程,希望这篇文章对于垃圾收集器如何工作的解释易于理解并且包含有充足的知识。

         这篇文章是我之前文章How Classpathworks in Java 和How to writeEquals method in java后续。在学习之前,让我们回忆几点关于垃圾收集器的重要知识:

1.      在java中,对象在堆里被创建,不管它是局部变量还是成员变量。值得注意的是在java内存空间的方法区创建的类变量和静态成员以及堆和方法区可以在不同线程之间共享。

2.      垃圾回收是java虚拟机提供的一种从合适于垃圾回收的对象处回收堆空间的一种机制。

3.      垃圾回收机制将程序员从C++程序的重要组成部分即内存管理中解脱出来,可以将更多的时间专注于业务逻辑。

4.      Java中的垃圾回收是通过一个称为垃圾回收器的守护线程运行的。

5.      在将一个对象从内存中移除之前,垃圾回收线程会调用这个对象的 finalize()方法,这也是给这个对象一个执行必要的清理工作的机会。

6.      作为一个java开发者,不能强行进行垃圾回收。只有当jvm根据堆空间想进行垃圾回收的时候才会进行。

7.      有一些方法例如System.gc () 和 Runtime.gc ()可以向JVM发送进行垃圾收集的请求,但并不能保证垃圾收集会立刻进行。

8.      如果在堆上没有更多的内存空间用于创建新对象,JVM会抛出OutOfMemoryError或者java.lang.OutOfMemoryError heap space。

9.      J2SE 5(Java 2 Standard Edition)添加了一个新特性叫做” Ergonomics”,它的目标是以最小的命令行调优得到更好的性能。

什么时候一个对象适合垃圾回收?

一个对象如果任何活动的线程或静态引用都不法访问,那么它是合适进行垃圾回收的。

或者说这个对象所有的引用都为null时,它是合适进行垃圾回收的。循环依赖不算是引用,如果对象A拥有对象B的引用,并且对象B拥有对象A的引用,而且他们都没有其他引用,那么对象A和对象B都是适合垃圾回收的。

         通常一个对象适合垃圾回收有以下几种情况:

1.      一个对象的所有引用被明确设置为null,例如object = null;

2.      在块中创建的对象。一旦程序从块中退出,那么这个对象的引用就会失效;

3.      父类对象被设置为null。如果一个对象包含另一个对象的引用,当你设置容器对象的引用为空,那么它的孩子或者被包含的对象自动变成适合垃圾收集的。

4.      如果一个对象只是通过WeakHashMap拥有一些活动的引用,那么它也是适合垃圾收集的。要学习更多关于HashMap的知识,可以看How HashMap works in Java

用于垃圾回收的堆中的各代

Java对象在堆中被创建,堆为了更好进行垃圾收集分为了三部分或者三代,它们称为:年轻代(Young generation),老年代(Tenured or Old Generation) 和(永久堆区)Perm Area of heap.

年轻代进一步被划分为三部分,称为: Eden spaceSurvivor 1 和 Survivor 2 space。当一个对象在堆中首先被创建的时候,它会被创建在年轻代中的Eden space。在随后的Minor Garbage collection中,如果这个对象幸存下来,那么它就会被移动到Survivor 1 ,然后是Survivor2 。最后经过Major Garbage collection,这个对象被移动到Old or tenured generation。

永久堆区(Permanent generation of Heap or Perm Area of Heap)多少有点特殊,它用于存储和JVM中的类和方法相关的元数据。它也用于存放JVM提供的字符串池,这点在我关于字符串的教程why String is immutable in Java有讨论。围绕在java永久堆区是否发生垃圾收集有很多观点,按照我的理解,这是JVM所依赖的东西,并且至少发生在Sun实现的JVM中。你也可以尝试通过创建数百万的字符串来试验一下,观察垃圾收集或者OutOfMemoryError错误。

Java中垃圾收集器的类型

Java Runtime (J2SE 5)提供了多种垃圾收集器类型,你可以根据你应用程序的性能要求进行选择。Java 5除了serial garbagecollector,还额外增加三种垃圾收集器。每个分代垃圾收集器被实现用于增加程序的吞吐量或者减少垃圾收集的暂停时间。

1) Throughput Garbage Collector :这个垃圾收集器使用一个young generation collector的平行版本。如果通过命令行选项将-XX:+UseParallelGC 传递给JVM,那么这个垃圾收集器就会被使用。老年代的垃圾收集器(tenuredgeneration collector)和串口垃圾收集器(serialcollector)是一样的。

2) Concurrent low pause Collector:如果-Xingc或 -XX:+UseConcMarkSweepGC通过命令行传递给jvm,这个垃圾收集器就会被使用。它也称为Concurrent Mark Sweep Garbage collector。 它被用于应用程序执行时的大部分并行收集和收集老年代(Tenured or Old Generation)。在进行垃圾收集的时候,应用程序会被暂停一个小的周期。一个年轻代复制收集器(young generation copying collector)的平行版本因为并发收集器被提出。并发标记扫描垃圾收集器( Concurrent Mark Sweep Garbage collector)被使用的最广泛的垃圾收集器。当垃圾收集器触发的时候,它使用算法先标记需要被收集的对象。

3) The Incremental (Sometimes called train) lowpause collector:只有XX:+UseTrainGC通过命令行传递给jvm时,这个收集器才会被使用。这个垃圾收集器自从java 1.4.2起就没有改变过,现在也没有再开发。在未来的版本中它将不会被支持,所以要避免使用它,可以阅读1.4.2 GC调优文档来获得关于这个收集器的知识。

重要的一点是, -XX:+UseParallelGC不应该和-XX:+UseConcMarkSweepGC一起使用(原文: Importantpoint to not is that -XX:+UseParallelGC shouldnot be used with -XX:+UseConcMarkSweepGC.)。这个参数传入J2SE平台始于1.4.2版本,仅允许垃圾收集器的命令行选项的合法组合,但是早期版本中可能不会找到或发现所有非法组合,这些非法组合的结果是不可预料的。总之这个垃圾收集器是不建议使用。

用于垃圾收集的JVM参数

垃圾收集调优(Garbage collection tuning)是一个长期的练习,需要耐心和对程序做大量性能分析才能使它正确。在处理高容量低延迟电子交易系统等这些我合作过项目时,我们通过性能分析和查找引起full GC的原因来提高应用程序的性能。我发现垃圾收集调优大量依赖于程序性能分析和是什么样的应用程序以及平均生命周期。例如如果应用有很多短暂的对象,那么需要使Edenspace足够广阔或将减少许多小的大集合。你也可以通过JVM参数控制年轻代和老年代的大小,例如设置 -XX:NewRatio=3,意味着年轻代和老年代的比例为1:3。在调整它们大小的时候需要你需要很小心。年轻代空间增加将会减少老年代空间的大小,这会引起主要收集发生的更加频繁,而在这期间暂停应用程序线程会导致退化或降低吞吐量。 参数NewSize 和 MaxNewSize用于指定年轻代尺寸的从下到上,设置这些相当于另一个修补年轻代的方法。我的观点是在进行垃圾收集调优之前详细了解垃圾收集是非常必要的,我建议阅读Sun公司提供的关于垃圾收集详细知识的文档,要得到一个特殊的JVM的完整参数列表请参考关于垃圾收集的官方文档。我发现这个链接是非常有用的 http://www.oracle.com/technetwork/java/gc-tuning-5-138395.html

Full GC和并发垃圾收集

并发垃圾收集器使用一个单一的垃圾收集线程,它是和应用程序线程并发的,目标是在老年代被填满之前完成对它的垃圾收集。在正常操作中,这个并发的垃圾收集器能在应用程序线程运行的情况下完成它大部分的工作,所以只需要应用程序线程做一些简短的暂停。相反,如果并发的垃圾收集器不能够在老年代被填满之前完成垃圾收集,那么这个应用程序就会暂停,在垃圾回收完成之前,所有应用线程都会停止。这种造成应用停止的垃圾收集称为full garbage collections 或 full GC ,这个迹象表明需要调整并发垃圾收集参数。永远要避免或最小化full garbage collections或 full GC,因为它会影响到java应用程序的性能。当你工作在金融领域的电子交易平台,高容量低延迟的java系统性能要求使避免在交易期间发生Full GC变得极为关键。

对垃圾收集的总结

1) java堆为了更好地进行垃圾收集分为了三代,它们称为:年轻代(Young generation),老年代( Tenured or Old Generation) 和(永久堆区)Perm Area of heap.

2)新对象在年轻代(young generation )中被创建,随后被转移到老年代( Tenured or Old Generation)。

3)字符串池被创建在永久堆区,垃圾收集会在永久堆区发生,但依赖于JVM。

4) Minor garbage collection用于将对象从 Eden space转移到Survivor 1Survivor 2,Major collection用于将对象从年轻代转移到老年代。

5)无论何时Majorgarbage collection发生,应用线程停止,在此期间,会降低应用程序的性能和吞吐量。

6)在java 6的垃圾收集机制中,一些性能提升已经被实现。我们通常使用JRE 1.6.20运行我们的应用。

7)JVM命令行参数–Xmx –Xms 被用于设置java堆的初始值和最大值,根据我的经验,这个参数的理想比例是1:1或者1:1.5。例如你可以设置–Xmx 和 –Xms 为 1GB 或者 –Xms 1.2 GB 和 1.8 GB.

8)没有手工的方法去进行垃圾收集。

Readmore:http://javarevisited.blogspot.com/2011/04/garbage-collection-in-java.html#ixzz2kt8GCSLo

原创粉丝点击