Java性能权威指南读书笔记--之二

新生代填满时,垃圾收集器会暂停所有的应用线程,回收新生代空间。这种操作被称为Minor GC。
老年代被填满时,垃圾收集器会暂停所有应用线程,对其进行回收,接着对堆空间进行整理。这个过程被称为Full GC。
最主流的四个垃圾收集器分别是:Serial收集器、Throughput(或者Parallel)收集器、Concurrent(CMS、G1)垃圾收集器。Concurrent垃圾收集器可以通过复杂的计算,可以在应用线程运行的同时找出不再使用的对象。
使用CMS或G1收集器时,应用程序精力的停顿会更少,所带来的代价是消耗更多的CPU。
评估垃圾收集器时,应考虑以下几点:

  • 如果要尽可能地缩短相应时间,那么选择使用Concurrent收集器更合适
  • 如果平均相应时间比最大响应时间更重要,那么应该使用Parallel收集器
  • 使用Concurrent收集器来避免长时间停顿时间也有其代价,这会消耗额外的CPU
    • 如果CPU足够强劲,使用Concurrent收集器避免发生Full GC可以让任务运行得更快
    • 如果CPU资源有限,那么Concurrent收集器额外的CPU消耗会使批量任务消耗更多的时间

GC算法

  1. Serial垃圾收集器
    使用单线程清理堆的内容。进行Full GC时,它还会对老年代空间的对象进行压缩整理。通过-XX:+UseSeralGC标志可以启用Serial收集器
  2. Parallel收集器
    Parallel收集器可以使用多线程对堆空间进行回收。在MinorGC和FullGC时会暂停所有的应用线程,同事在FullGC过程中会对老年代空间进行压缩整理。通过-XX:+UseParallelOldGC标志可以开启这个功能。
  3. CMS收集器
    CMS收集器在MinorGC时会暂停所有的应用线程,并以多线程的方式进行垃圾回收(-XX:UseParNewGC)。CMS收集器在进行FullGC时不再暂停应用线程,而是使用若干个后台线程定期地对老年代空间进行扫描, 及时回收其中不在使用的对象,并且后台线程不在进行任何压缩整理的工作。如果堆的碎片化过于严重CMS收集器会暂停所有应用线程,使用单线程回收、整理老年代空间(-XX:UseConcMarkSweepGC)。
  4. G1垃圾收集器
    G1收集算法将堆划分为若干个区域,新生代的垃圾收集仍然采用暂停所有应用线程的方式,将存货对象移动到老年代或者Servivor空间。老年代的垃圾收集工作由后台线程完成,由于老年代被划分到不同的区域,G1收集器通过将对象从一个区域复制到另一个区域,完成对象的清理工作,同时在进行了堆的压缩整理(-XX:+UseG1GC)。

选择GC算法

如果有额外的CPU处理能力,那么使用Concurrent收集器将极大地提升应用程序的性能。
通常情况下,parallel收集器的平均响应时间比Concurrent收集器要差,但是在90%响应时间或者99%响应时间这几项指标上,parallel收集器比Concurrent收集器要好一些。
使用parallel收集器会超负荷地进行大量Full GC时,切换到Concurrent收集器通常能获得更低的响应时间。
一般情况下,堆空间小于4GB时,CMS收集器的性能比G1收集器好。
G1的设计使得它能够在不同的分区处理堆,因此它的扩展性更好,比CMS更易于处理超大堆的情况。

调整堆的大小

通常情况下,对于普通的操作系统,应该预留至少1G的内存空间。
堆的大小由2个参数值控制:分别是初始值(通过 -Xms N设置)和最大值(通过-Xms N设置)。
如果确切地了解应用程序需要多大的堆,那么可以将堆的初始值和最大值直接设置成对应的数据(譬如:-Xms4096m -Xmx4096m)。这种设置能稍微提高GC的运行效率,因为它不再需要估算堆是否需要调整大小了。
任何事情都有两面性,如果新生代分配得比较大,垃圾收集发生的频率就比较低,从新生代晋升到老年代的对象也更少,但是老年代就相对比较小,比较容易被填满,会更频繁的触发Full GC。
所有用于调整代空间的命令行标志调整的都是新生代空间;新生代空间剩下的所有空间都被老年代占用。
-XX:NewRatio=N
设置新生代与老年代的空间占用比率
-XX:NewSize=N
设置新生代空间的初始大小
-XX:MaxNewSize=N
设置新生代空间的最大大小
-XmnN
将NewSize和MaxNewSize设定为同一个值的快捷方法
最初的新生代的大小由NewRatio的决定,默认值为2.
Initial Young Get Size = Initial Heap Size/(1 + NewRatio)
新生代空间的大小是初始堆大小的33%
多线程的垃圾收集器算法是由-XX:ParallelGCThreads=N参数控制
总的垃圾收集器线程数 = 8 + (N - 8)* 5/8
有时候垃圾收集线程需要调整,例如在128C的机器上使用一个1G的堆,那么会启动83个线程,明显就偏大了;又如在16C的机器同时运行4个JVM实例,那么每个JVM会启动13个垃圾收集器线程,总共会有52个垃圾收集器线程,这样会导致大量冲突,那么将每个JVM的垃圾收集器限制为4个是一个比较合理的平衡。

垃圾回收工具

-XX:+PrintGCDetails
创建详细的GC日志
-XX:+PrintGCTimeStamps -XX:+PrintGCDateStamps
GC时发生的日期,可以配合上面的参数使用
-Xloggc:filename,可以配合上面的参数使用
修改GC日志输出到某个文件
-XX:+UseGCLogFileRotation
-XX:NumberOfGCLogFiles=N
-XX:GCLogFileSize=N
可以控制日志文件的循环。NumberOfGCLogFiles产生日志的个数,GCLogFileSize日志的大小

你可能感兴趣的:(Java性能权威指南读书笔记--之二)