关于垃圾回收(GC)的7个错误认识

已有 7150人阅读此文 - - JAVA


    当我还是小孩子的时候我的父母经常在我不学习的时候告诉我,如果不好好学习以后就要去去清理垃圾。然而,他们却不知道清理垃圾真是一件很棒的事(因为有了清洁工人我们的世界才变的这么干净,要不然你能想像吗?向清洁工致敬。)。也许正因为此在java开发者中对垃圾回收GC充满很多疑问:GC是怎么工作的、GC对我的应用程序起到怎样的作用、我能为此做什么。

在性能调优指南中,我们收集了一些关于垃圾收集的认识,并在其中选取了7个误区,来说明他们为什么是错的:

1.只有一个垃圾收集器

    只有一个肯定是不对的,只有4个也不是正确的答案。我之前翻译过一个关于hotspot jvm有四个垃圾收集器的文章:垃圾收集器Serial 、Parallel、CMS、G1 。但是还有更多,还有很多非标准的垃圾收集器以及一些大胆的实现如Shenandoah 或其它jvm使用的收集器。HotSpot jvm默认的收集器是Parallel / Throughput并行/吞吐收集器,但通常这对你的应用并不是最优选项。例如:CMS(Concurrent Mark Sweep)和G1(Garbage First)不会造成频繁的暂停。但当暂停发生时,它的持续时间会比并行收集器(Parallel)更长。另一方面,并行收集器在相同堆大小的情况下能达到更大的吞吐量。

PS:为你应用选择正确的垃圾收集器依赖于你的需求:所能接受的GC暂停频率及暂停的持续时间。

2.并行(parallel)=并发(concurrent)

一个垃圾收集的周期要么是STW(Stop-The World)及产生一个GC暂停,要么是并发运行同时不会停止应用程序。GC算法要么是串行(serial,单线程)要么是并行(parallel,多线程)。这就是为什么当我们提到并发GC时,并不意味着它是并行的,而当我们提到串行时也并不意味着会发生暂停。在GC的过程中,并发和并行是两个绝对不同的术语:提到的并行过程或并行过程只和相对应的GC算法本身有关。

译者注:

    并发和并行的区别:前者是一个处理器同时处理多个任务,后者是多个处理器或者是多核的处理器同时处理多个不同的任务。前者是逻辑上的同时发生(simultaneous),而后者是物理上的同时发生.

并发,就像一个人(cpu)喂2个孩子(程序),轮换着每人喂一口,表面上两个孩子都在吃饭。并行,就是2个人喂2个孩子,两个孩子也同时在吃饭。

    所谓“并发”,是指一个过程在 **逻辑上** 是由许多同时执行的线索组成的,但是在 **物理上** 这些线索不一定是同时执行的。比如在单处理器机器上,我们的进程、线程只能并发,不能并行。至于“并行”,则恰恰相反,是指一个过程在 **物理上** 包含了许多同时进行的工作,但是在 **逻辑上**并不一定是包含许多同时执行的线索的。

并行任务单个任务都是串行的,一般不互相影响;并发任务之间一般会出现互斥现象,及锁的争用。

PS:GC是分为两步的,调用GC执行周期的方式和运行业务逻辑的方式是不同的两个事情。

 

3.G1垃圾收集器可以解决所有问题

    在jdk7中有一些更新和修改,G1垃圾收集器就是最新加入到JVM垃圾收集器序列的。G1收集器最大的优势是它解决了CMS垃圾收集器收集过程中的碎片化问题:GC周期会从老年代(Old Generation)中释放内存块,结果内存变得像瑞士奶酪那样千疮百孔,直到JVM对其无从下手了,才不得不停下来处理这些碎片。但是故事没这么简单,某些情况下其他回收器可能比G1有更好的表现,这完全取决于你的需求。

PS:没有一个万能的方案能解决GC问题,需要通过不断的实验去选择最适合自己的收集器。

4.平均事务时间是最重要的检测指标

    如果你在服务器仅仅监测平均事务运行时间,你就错过了异常值。对于用户来说这种意识是毁灭性的。例如,一个事务平时的运行时间是低于100ms, 在受到GC暂停的影响时,需要1分钟才能完成。如果你仅仅监测事务平均时间,你是无法觉察到这些普通用户可以轻易发现的问题。试想现在有1%或者更多点的用户经历这样的场景,当你仅仅监测事务平均时间,你会发现这种场景很容易被忽略。

PS:留心异常情况,了解99%情况下你的系统运行状况(不应该是1%)。

5. 减少新的对象分配率将提高GC行为

    我们可以把系统中的对象划分为3类:长存活对象,对此我们没有太多事情可以做;中等存活对象,最大的问题会放生在这里;短存活对象,这些通常释放和分配较快,因此将会在下次一次的GC中被处理。关注中等生命周期的对象的分配率将会带来积极的结果,而集中关注点在长存活对象和短存活对象通常被证明无太大用处的,同时控制中存活对象通常是一个艰巨的任务。

PS:单纯的关注对象分配率并不是减轻服务器压力的办法,对象的种类才是引起问题的根源。

6.调优可以解决任何事情

    如果你的应用需要保存一个大的被频繁改变的状态,在这种情况下通过对JVM堆内存调优并不能收获到太多的好处,长时间的GC暂停也就在所难免。一个解决办法可以对架构进行改变,保证一个对响应时间有决定性影响或者造成瓶颈的过程中,不包含大量状态。大量状态和响应能力是难以良好共存的,因此将它们分开处理才是很好的选择方案。

PS:不是所有问题都可以通过调整JVM参数来完成,有时需要重新考虑一下自己的设计思路。

7.GC日志造成很大的开销

    简单的说,这是错误的,尤其在默认的日志配置下。日志数据是很有价值的,而且jdk7中引入了勾子的概念来控制日志的尺寸并保证日志量的上升不会用光你的硬盘空间。如果你未收集GC日志数据,那么你会失去这几乎是唯一的,知晓JVM垃圾回收器在生产环境中工作状态的方法。一般可接受的GC开销以5%作为上限,如果你能知道系统为GC停顿付出的代价,也能对最小化这个代价采取行动,这种程度的开销是不值一提的。

PS:在能力范围内,尽可能多地获取系统在生产环境中的运行数据,你会发现那是一个全新的世界。

总结

    希望这些介绍可以帮助你更好地掌握怎么进行垃圾回收的工作。在你的应用程序你认识到这些问题了吗?你是否发现了更多的垃圾收集的误区?可以通过在本文后面的评论中来一起讨论。

 

  

英文原文地址:http://blog.takipi.com/7-things-you-thought-you-knew-about-garbage-collection-and-are-totally-wrong/


来源:自成e家 出处:关于垃圾回收(GC)的7个错误认识
本文由 自成e家 翻译 ,转载请注明出处,你的支持是我继续写作、分享的最大动力!
期待你一针见血的评论,Come on!