[关闭]
@xtccc 2018-05-15T08:56:10.000000Z 字数 4673 阅读 3815

GC经验之谈

给我写信
GitHub

此处输入图片的描述


Java



参考:



好文



本文内容针对Java 8

1. 这些日子遇到的坑


1.1 别让-Xms太大

如果把-Xms搞得太大,达到了系统的极限,Java进程会被kernel Kill。如果时通过控制台启动的Java进程,会在console上看到:

image_1bfmqku8vrh71vp01h3ql8av2qp.png-38.8kB

Java进程直接被kernel杀掉,因此,即使设置了-XX:ErrorFile或者-XX:+HeapDumpOnOutOfMemoryError之类的选项,你也不会看到error文件的生成。

通过dmesg命令,可以看到kernel的信息,例如:
image_1bfmt3fak105acgf7r1oa81l139.png-89.6kB

也可能看到如下的输出:
image_1bfmteerq17crp2i1u851g1c9eo16.png-129.9kB

参考 Who “Killed” my process and why?

1.2 别把-Xmx设置得太大

-Xmx应该根据实际的需求来设置,而不是越大越好。如果设置地过大,可能反而导致throughput变低,或者latency变高。

1.3 不要过度优化

有些优化选项伴随着某方面的代价,开启后会造成性能下降。
例如,当使用G1 collector时,如果使用了-XX:+UseStringDeduplication,那么throughput会显著地降低


2 Basics


2.1 JVM结构

image_1bft404vpd499cc1foa1n4v1gad23.png-70.2kB

紫框的3个部分就是对性能影响最大的3个部分。

2.2 GC调优,究竟调什么?

GC调优,就是:

  1. 调整Heap的大小,以及Heap中各个generation的大小
  2. 选择最合适的garbage collector

JIT compiler对Java App的性能也有很大影响,但是它极少需要去调优。

2.3 GC的大体过程

GC的目的是:在Heap中识别哪些object是有用的,哪些是没用的。无用objects,就是那些没有被程序的任何部分引用(referenced)的objects。unreferenced objects将要被删除,它们占据的heap空间将被释放。

GC的过程可以分为以下几个步骤:

2.4 Generations

以上的三个步骤都是相对耗时的操作,根据“大部分的objects都是短命鬼”的经验,JVM将heap划分成了3个部分:

image_1bft7rrpe1k3t3f0rsr186o14fi2g.png-13.8kB

2.5 GC的真正步骤

  1. 新的对象都会被分配到Eden区域,S0和S1初始时都是空的。
  2. 当Eden区域被填满之后, 将触发一次minor GC。
  3. referenced objects将被移动至S0,而unreferenced objects则将被删除,此时Eden已被清空。
  4. 下一次minor GC发生时,Eden被清空,其中的referenced objects被移动至S1。并且,上次minor GC留在S0中的对象将被移动至S1。
  5. 再下一次minor GC发生时,会重复类似的过程,只不过这次S0和S1将被交换:referenced objects将被移动至S0,S1和Eden被清空。
  6. 在每一次minor GC发生时,如果对象的年纪到了某个阀值,它将被从年轻代提升至老年代。
  7. 最终,major GC 将在老年代中发生,这将清理老年代,并对碎片区域进行合并。

2.6 Parallel Collectors

在单核机器上,只能使用默认的Serial Collector: -XX:+UseSerialGC

在多核机器上,则可以使用Parallel Collector (又称为throughput collector)。有多种Parallel collectors可以使用。

  1. "-XX:+UseParallelGC"
    这个设置,会对young generation使用multi-thread collector,而对old generation则仍然使用single-thread collector及single-thread compaction

  2. "-XX:+UseParallelOldGC"
    这个设置,会对young generation collection、old generation collection以及old generation compaction都使用multi-thread collector。

  3. "-XX:+UseConcMarkSweepGC"
    这个设置,将会对old generation使用Concurrent Mark Sweep (CMS) Collector,而对young generation则仍然使用原来的parallel collector。CMS的特点是low pause,因为它不会进行compaction(即不会移动live objects),因此heap fragments可能会是一个问题。在这种情况下,需要使用更大的heap。
    CMS Collector将被G1 Collector代替,下面会讲。

  4. "-XX:+UseG1GC"
    G1 collector is a parallel, concurrent, and incrementally compacting low-pause garbage collector.


3 正确理解Generations


2.1 "-Xmx" & "-Xms"

以下述仅适用于serial, stop-the-world(non-parallel) collectors,参考 Sizing the Generations

At initialization of the virtual machine, the entire space for the heap is reserved. The size of the space reserved can be specified with the -Xmx option. If the value of the -Xms parameter is smaller than the value of the -Xmx parameter, than not all of the space that is reserved is immediately committed to the virtual machine. The uncommitted space is labeled "virtual" in the following figure. The different parts of the heap (tenured generation and young generation) can grow to the limit of the virtual space as needed.(两头向中间发展)

image_1bft15eu313b51jf917bh2t2tnd9.png-45.5kB



对于



4 分析GC log


如果指定了-XX:+PrintGCDetails -Xloggc:${RootDir}/akka/logs/worker-gc.log,则可以将GC log输出到文件中。为了防止输出的文件太大,还可以继续加入参数-XX:+UseGCLogFileRotation -XX:NumberOfGCLogFiles=3 -XX:GCLogFileSize=10M

GC log里面的内容形如:
image_1bftil6kk1k30evf16jj9il1o839.png-412.1kB

G1 Collector输出的GC log则比较特殊:
image_1bfts5gdnb3k1qnq19gk59t1uv620.png-257.8kB



我们可以通过一些工具来分析这些log,并且找出哪里是瓶颈。GCeasy就是这样的一个工具,在网站上我们把GC log文件上传,然后它就会分析,并且指出可疑之处。
image_1bftjpdjg1qop4ekbn19mb12gg13.png-138.1kB


5 选择哪种collector ?


5.1 G1 Collector

GC pause time可以显著降低,

同一段代码和同一份数据,使用-XX:UseParallelOldGC时,max gc pause time ≈ 4秒;换成-XX:UseG1GC,则max gc pause time ≈ 0.4秒

因此, G1适合于对响应时间要求高的应用。如果跑batch计算,G1就不合适。



5.2 CMS Collector

Useful JVM Flags – Part 7 (CMS Collector)

一般来说,CMS GC一般比其他parallel GC更快,特别是full GC更快。但是当concurrent mode failure发生时,CMS GC比其他parallel GC要慢。

CMS GC与Parallel GC的区别在于compaction. Parallel GC会在发生Full GC时,同时发生compaction。CMS GC则不会立即进行compaction。

因此,CMS GC不会立即对内存碎片进行清理,而是等到无法再分配新内存时(i.e., concurrent mode failure)再进行compaction然后再试图分配内存。



6 内存泄漏怎样找?


参考 DIAGNOSE MEMORY LEAK



7 吞吐量低怎么办?


参考 INCREASE THROUGHPUT



添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注