JVM笔记10-性能优化之高级特性

一.垃圾回收器配置和 GC 日志分析 1.堆典型配置: 32位的操作系统限制堆大小介于1.5G到2G,64位操作系统无限制,同时系统可用虚拟内存和可用物理内存都会限制最大堆的配置。 堆空间分配典型配置:   1.-Xms:初始堆大小   2.-Xmx:最大堆大小   3.-XX:NewSize=n:设置年轻代大小   4.-XX:NewRatio=n:设置年轻代和年老代的比值。如 n 为 2,表示年轻代与年老代比值为 1:2,年轻代占整个年轻代年老代和的 1/3   5.-XX:SurvivorRatio=n:年轻代中 Eden 区与两个 Survivor 区的比值。注意 Survivor 区有两个。如 n 为 2,表示 Eden:Survivor=1:2,一个 Survivor 区占整个年轻代的 1/4   6.-XX:MaxMetaspaceSize=:设置最大化元空间大小   7.XX:MetaspaceSize=64m 初始化元空间大小;   8.-Xmn2g:设置年轻代大小为 2G。整个 JVM 内存大小 = 年轻代大小 + 年老代大小 + 元空间大小。元空间一般固定大小为 64m,所以增大年轻代后,将会减小年老代大小。此值对系统性能影响较大,Sun 官方推荐配置为整个堆的 3/8。   9.-Xss128k:设置每个线程的栈大小。JDK5.0 以后每个线程堆栈大小为 1M,以前每个线程堆栈大小为 256K。在相同物理内存下,减小这个值能生成更多的线程。但是操作系统对一个进程内的线程数还是有限制的在 3000~5000 间。   10.-XX:SurvivorRatio=4:设置年轻代中 Eden 区与 Survivor 区的大小比值。   11.-XX:MaxMetaspaceSize=16m:设置元空间大小为 16m。   12.-XX:MaxTenuringThreshold=0:设置年轻代最大年龄。如果设置为 0 的话,则年轻代对象不经过 Survivor 区,直接进入年老代。对于年老代比较多的应用,可以提高效率。如果将此值设置为一个较大值,则年轻代对象会在 Survivor 区进行多次复制,这样可以增加对象再年轻代的存活时间,增加在年轻代即被回收的概率。   13.-XX:+DisableExplicitGC:这个将会忽略手动调用 GC 的代码使得 System.gc() 的调用就会变成一个空调用,完全不会触发任何 GC 复制代码 -Xmx5120m –Xms5120m -Xmn2g -Xss128k -XX:NewRatio=4 -XX:SurvivorRatio=4 -XX:MaxMetaspaceSize=16m -XX:MaxTenuringThreshold=0 复制代码 2.垃圾回收器配置:   1.Serial 收集器:     -XX:MaxTenuringThreshold 默认值是 15,新生代对象晋升为老年代对象需要经过 15 次 GC。   2.ParNew 收集器:     -XX:MaxTenuringThreshold 默认值是 15,新生代对象晋升为老年代对象需要经过 15 次 GC。     -XX:UseAdaptiveSizePolicy JVM 根据运行参数,动态调整堆空间大小及晋升年龄值。   3.Parallel Scavenge/Parallel Old 收集器:     -XX:ParallelGCThreads 设置并发收集器年轻代收集方式为并行收集时,使用的CPU数。并行收集线程数。     -XX:UseAdaptiveSizePolicy JVM根据运行参数,动态调整堆空间大小及晋升年龄值     -XX:MaxTenuringThreshold 默认值是15,新生代对象晋升为老年代对象需要经过15次GC     -XX:GCTimeRatio 设置垃圾回收时间占程序运行时间的百分比(默认99),公式为1/(1+n)     -XX:MaxGCPauseMillis 设置并行收集最大暂停时间。   4.CMS 收集器:     -XX:ParallelCMSThreads 垃圾收集器线程数     -XX:CMSFullGCsBeforeCompaction CMS 采用标记-清理算法,会产生内存碎片,配置执行多少次 FullGC 后对内存进行整理。     -XX:UseCMSCompactAtFullCollection 配置 FullGC 后是否立即整理内存碎片。     -XX:CMSInitiatingOccupancyFraction 配置老年代内存使用率达到多少后进行内存回收( JDK6 及以上版本默认值 92%)。     -XX:CMSInitiatingOccupancyOnly 默认 false,不允许 HostSpot 根据成本自行进行决定何时进行垃圾回收。     -XX:CMSClassUnloadingEnabled 配置方法区使用 CMS 进行垃圾回收。     -XX:+CMSIncrementalMode 设置为增量模式,适用于单 CPU 情况。     -XX:+PrintGCApplicationStoppedTime 打印程序 Stop-the-World 的暂停时间。   5.G1 收集器:     -XX:G1ReservePercent 默认值 10%,预留的空闲空间的百分比。     -XX:G1HeapRegionSize 配置 Region 块的大小,范围 1MB 到 32MB,设置后会根据最小堆 Java 堆内存划分出 2048 个 Region 块。 3.垃圾收集统计配置:    -XX:+PrintGC    -XX:+PrintGCDetails    -XX:+PrintGCTimeStamps:可与上面参数一起使用    -XX:+PrintGCApplicationConcurrentTime:打印每次垃圾回收前,程序未中断的执行时间,可与上面参数一起使用    -XX:+PrintGCApplicationStoppedTime:打印垃圾回收期间程序暂停的时间,可与上面参数一起使用    -XX:PrintHeapAtGC:打印 GC 前后的详细堆栈信息    -Xloggc:filename:与上面几个配合使用,把日志信息记录到文件来分析    通过设定 -XX:+UseG1GC 在整个 Java 堆使用 G1 进行垃圾回收    通过 -XX:+UseConcMarkSweepGC 设定新生代使用 ParNew(并发复制)收集器,老年代使用 CMS Concurrent Mark-Sweep(并发标记清除)收集器执行内存回收    通过 -XX:+UseParallelOldGC 手动指定新生代使用 Parallel Scavenge(并行复制)收集器,老年代使用 Parallel Old(并行标记-压缩)收集器执行内存回收    通过 -XX:+UseSerialGC 手动指定新生代使用 Serial Coping(串行复制)收集器,老年代使用 Serial Old (串行标记-清理-压缩)收集器执行内存回收    通过 -XX:+UseParNewGC 手动指定新生代使用 ParNew(并发复制)收集器,老年代使用 Serial Old (串行标记-清理-压缩)收集器执行内存回收    通过 -XX:+UseParallelGC 手动指定新生代使用 Parallel Scavenge(并行复制)收集器,老年代使用 Serial Old (串行标记-清理-压缩)收集器执行内存回收 4.GC日志分析 复制代码 /** * Created by cong on 2018/8/1. * * 堆溢出 * 通过run configurations配置下列参数 * VM Args:-Xms20M -Xmx20M -Xmn10M -XX:+PrintGCDetails * -XX:SurvivorRatio=8 -XX:+UseSerialGC * -XX:+HeapDumpOnOutOfMemoryError * 参数-XX:+HeapDumpOnOutOfMemoryError可以让虚拟机在出现内存溢出异常时Dump出当前的内存堆转储快照以便事后进行分析,文件在项目中lib目录的外层目录下 */ public class HeapOutOfMemory { static class OutOfMemoryObject { } public static void main(String[] args) { List list = new ArrayList(); while (true) { list.add(new OutOfMemoryObject()); } } } 复制代码 在IDE上面配置虚拟机参数,如下: 然后点击 run,如果报错,那就是虚拟机参数里带了中文标点或者参数名字错了,使用 Serial Coping(串行复制)/Serial Old (串行标记-清理-压缩)组合打印的日志,日志跟下面的使用 -XX:+UseParallelOldGC 打印的日志类似,参考下面 Parallel 的日志分析。 -XX:+UseParNewGC 使用 ParNew(并发复制)/Serial Old (串行标记-清理-压缩),日志跟下面的使用 -XX:+UseParallelOldGC 打印的日志类似,参考下面 Parallel 的日志分析。 -XX:+UseParallelOldGC 使用 Parallel Scavenge(并行复制)/ Parallel Old(并行标记-压缩) 。日志分析如下: 复制代码 [GC [PSYoungGen: 7469K->1016K(9216K)] 7469K->5241K(19456K), 0.0110178 secs] [Times: user=0.05 sys=0.02, real=0.01 secs] /* * [PSYoungGen: 7469K->1016K(9216K)] PSYoungGen在新生代发生Minor * GC,回收前内存占用7469K,回收后内存占用1016K,新生代的总内存大小 7469K->5241K(19456K) * Java整个堆空间的内存占用变化,回收前7469K,回收 后5241K,整个堆19456K 0.0110178 secs,整个Minor * GC耗时多少秒,[Times: user=0.05 sys=0.02, real=0.01 secs] user程序耗时,sys系统耗时,real实际耗时 * 多少秒 */ [GC-- [PSYoungGen: 9208K->9208K(9216K)] 13433K->19440K(19456K), 0.0166318 secs] [Times: user=0.02 sys=0.00, real=0.02 secs] [Full GC [PSYoungGen: 9208K->0K(9216K)] [ParOldGen: 10232K->10207K(10240K)] 19440K->10207K(19456K) [PSPermGen: 2632K->2631K(21504K)], 0.1698064 secs] [Times: user=0.34 sys=0.00, real=0.17 secs] [Full GC [PSYoungGen: 7735K->7728K(9216K)] [ParOldGen: 10207K->8097K(10240K)] 17942K->15826K(19456K) [PSPermGen: 2631K->2631K(21504K)], 0.1632131 secs] [Times: user=0.42 sys=0.00, real=0.15 secs] /* * Full GC代码整个堆的GC,PSYoungGen新生代内存使用变化,ParOldGen老年代内存使用变化,PSPermGen永久代内存使用变化,0. * 1632131 secs本次Full GC消耗多少秒,Times: user=0.42 sys=0.00, real=0.15 secs 本次Full * GC各种时间消耗,所有参数含义跟上面一样。 */ [Full GC [PSYoungGen: 7728K->7728K(9216K)] [ParOldGen: 8097K->8089K(10240K)] 15826K->15818K(19456K) [PSPermGen: 2631K->2631K(21504K)], 0.0952587 secs] [Times: user=0.33 sys=0.02, real=0.10 secs] java.lang.OutOfMemoryError: Java heap space // 生成的堆快照文件java_pid26276.hprof,在lib目录下 Dumping heap to java_pid26276.hprof ... 复制代码 -XX:+UseConcMarkSweepGC 使用ParNew(并发复制)/ CMS Concurrent Mark-Sweep(并发标记清除),日志如下: 复制代码 [GC[ParNew: 7469K->1024K(9216K), 0.0339335 secs] 7469K->7079K(19456K), 0.0339810 secs] [Times: user=0.06 sys=0.00, real=0.03 secs] // 新生代使用ParNew做垃圾回收,参数含义跟上面分析的一样 Total time for which application threads were stopped: 0.0341067 seconds /* 程序暂停时间0.0341067秒,-XX:+PrintGCApplicationStoppedTime打印程序Stop-the-World的暂停时间 */ [GC[ParNew: 9216K->9216K(9216K), 0.0000205 secs][CMS: 6055K->8941K(10240K), 0.0319501 secs] 15271K->13881K(19456K), [CMS Perm : 2630K->2629K(21248K)], 0.0320168 secs] [Times: user=0.03 sys=0.00, real=0.03 secs] Total time for which application threads were stopped: 0.0321285 seconds [GC [1 CMS-initial-mark: 8941K(10240K)] 13881K(19456K), 0.0035077 secs] [Times: user=0.02 sys=0.00, real=0.01 secs] /*CMS初始标记,老年代内存占用大小8941K,总大小10240K,标记跟根对象集合直接相连接的对象的可达性*/ Total time for which application threads were stopped: 0.0035747 seconds [Full GC[CMS[CMS-concurrent-mark: 0.011/0.011 secs] [Times: user=0.01 sys=0.00, real=0.01 secs] //并发标记,从上次初始标记对象出发,标记垃圾对象和可回收对象 (concurrent mode failure): 8941K->8940K(10240K), 0.0385219 secs] 14326K->13896K(19456K), [CMS Perm : 2632K->2632K(21248K)], 0.0385659 secs] [Times: user=0.03 sys=0.00, real=0.03 secs] /* * 并发标记期间,新生成的对象在老年代无法有足够的内存容纳,产生concurrent mode * failure,老年代改用串行收集器,如果不产生concurrent mode * failure,后面还有CMS-remark,做最终标记,修正标记,会有暂停时间和内存占用和总内存,最后就是CMS-cocurrent-sweep, * 并发清除,会有清除耗时 */ Total time for which application threads were stopped: 0.0387397 seconds 复制代码 接着我们将上面的例子进行改造,改造如下: 复制代码 /** * Created by cong on 2018/8/1. * * 堆溢出 * 通过run configurations配置下列参数 * VM Args:-Xms20M -Xmx20M -Xmn10M -XX:+PrintGCDetails * -XX:SurvivorRatio=8 -XX:+UseConcMarkSweepGC * -XX:+HeapDumpOnOutOfMemoryError * 参数-XX:+HeapDumpOnOutOfMemoryError可以让虚拟机在出现内存溢出异常时Dump出当前的内存堆转储快照以便事后进行分析,文件在项目中lib目录的外层目录下 */ public class HeapOutOfMemory { static class OutOfMemoryObject { public byte[] spaceSize = new byte[1024 * 1024]; // 产生1024*1024个字节,也就是1024*1KB的内存 ,大小1MB } public static void creatHeap(int num) throws Exception { ArrayList list = new ArrayList(); for (int i = 0; i < num; i++) { list.add(new OutOfMemoryObject()); } System.gc(); } public static void main(String[] args) throws Exception { while (true) { creatHeap(99); } } } 复制代码 运行结果的,日志分析如下: 复制代码 [GC[ParNew: 8111K->8111K(9216K), 0.0000122 secs][CMS: 7176K->9216K(10240K), 0.0104259 secs] 15287K->14839K(19456K), [CMS Perm : 2632K->2632K(21248K)], 0.0104885 secs] [Times: user=0.01 sys=0.00, real=0.01 secs] Total time for which application threads were stopped: 0.0106245 seconds [GC [1 CMS-initial-mark: 9216K(10240K)] 15863K(19456K), 0.0004189 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] Total time for which application threads were stopped: 0.0006342 seconds [Full GC[CMS[CMS-concurrent-mark: 0.002/0.002 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] (concurrent mode failure): 9216K->9216K(10240K), 0.0043732 secs] 16947K->16887K(19456K), [CMS Perm : 2632K->2632K(21248K)], 0.0044245 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] java.lang.OutOfMemoryError: Java heap space Dumping heap to java_pid1264.hprof ... Total time for which application threads were stopped: 0.0171680 seconds Heap dump file created [17857505 bytes in 0.018 secs] [GC [1 CMS-initial-mark: 9216K(10240K)] 16876K(19456K), 0.0005822 secs] [Times: user=0.02 sys=0.00, real=0.00 secs] Total time for which application threads were stopped: 0.0014073 seconds [CMS-concurrent-mark: 0.002/0.002 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] [CMS-concurrent-preclean: 0.001/0.001 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] [CMS-concurrent-abortable-preclean: 0.000/0.000 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] [GC[YG occupancy: 7863 K (9216 K)][Rescan (parallel) , 0.0002252 secs][weak refs processing, 0.0000048 secs][scrub string table, 0.0001071 secs] [1 CMS-remark: 9216K(10240K)] 17079K(19456K), 0.0003718 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] /* * CMS-remark,做最终标记,修正标记,老年代已用内存9216K,总内存10240K,耗时 0.0003718 */ Total time for which application threads were stopped: 0.0005235 seconds [CMS-concurrent-mark: 0.002/0.002 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] /* * CMS-cocurrent-sweep,并发清除耗时0.002秒 */ 复制代码 通过设定 -XX:+UseG1GC 在整个 Java 堆使用 G1 进行垃圾回收,日志分析如下: 复制代码 [GC pause (young) (initial-mark), 0.0028719 secs] [Parallel Time: 2.2 ms, GC Workers: 4] [GC Worker Start (ms): Min: 168.7, Avg: 168.8, Max: 168.9, Diff: 0.2] [Ext Root Scanning (ms): Min: 0.9, Avg: 1.1, Max: 1.5, Diff: 0.6, Sum: 4.6] [Update RS (ms): Min: 0.0, Avg: 0.0, Max: 0.0, Diff: 0.0, Sum: 0.0] [Processed Buffers: Min: 0, Avg: 2.0, Max: 8, Diff: 8, Sum: 8] [Scan RS (ms): Min: 0.0, Avg: 0.0, Max: 0.0, Diff: 0.0, Sum: 0.0] [Object Copy (ms): Min: 0.1, Avg: 0.3, Max: 0.5, Diff: 0.4, Sum: 1.3] [Termination (ms): Min: 0.0, Avg: 0.4, Max: 0.5, Diff: 0.5, Sum: 1.4] [GC Worker Other (ms): Min: 0.0, Avg: 0.0, Max: 0.0, Diff: 0.0, Sum: 0.1] [GC Worker Total (ms): Min: 1.6, Avg: 1.8, Max: 2.0, Diff: 0.4, Sum: 7.3] [GC Worker End (ms): Min: 170.3, Avg: 170.6, Max: 170.8, Diff: 0.5] [Code Root Fixup: 0.0 ms] [Clear CT: 0.1 ms] [Other: 0.6 ms] [Choose CSet: 0.0 ms] [Ref Proc: 0.5 ms] [Ref Enq: 0.0 ms] [Free CSet: 0.0 ms] [Eden: 1024.0K(10.0M)->0.0B(9216.0K) Survivors: 0.0B->1024.0K Heap: 4857.6K(20.0M)->4720.2K(20.0M)] [Times: user=0.00 sys=0.00, real=0.00 secs] Total time for which application threads were stopped: 0.0036639 seconds // 初始标记以及暂停时间0.0036639 seconds [GC concurrent-root-region-scan-start] [GC concurrent-root-region-scan-end, 0.0005495 secs] // 扫描根region,耗时0.0005495 secs [GC concurrent-mark-start] [GC concurrent-mark-end, 0.0004523 secs] // 并发标记,耗时0.0004523 secs [GC remark [GC ref-proc, 0.0000279 secs], 0.0006380 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] Total time for which application threads were stopped: 0.0008879 seconds [GC cleanup 6809K->6809K(20M), 0.0006380 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] // 清除阶段 Total time for which application threads were stopped: 0.0006810 seconds [GC pause (young) (to-space exhausted), 0.0044659 secs] [Parallel Time: 4.0 ms, GC Workers: 4] [GC Worker Start (ms): Min: 176.6, Avg: 176.6, Max: 176.6, Diff: 0.0] //Parallel Time 并行处理的部分占用时间,Worker Start – 工作线程启动的时刻 [Ext Root Scanning (ms): Min: 0.3, Avg: 0.3, Max: 0.3, Diff: 0.1, Sum: 1.1] //External root scanning 扫描外部根锁使用的时间 [Update RS (ms): Min: 0.0, Avg: 0.0, Max: 0.0, Diff: 0.0, Sum: 0.0] //Update Remembered Set 开始前更新缓存列表,后续并发线程可以正确处理 [Processed Buffers: Min: 0, Avg: 0.5, Max: 2, Diff: 2, Sum: 2] [Scan RS (ms): Min: 0.0, Avg: 0.0, Max: 0.0, Diff: 0.0, Sum: 0.0] //Scanning Remembered Sets 查询指向收集区域的指针. [Object Copy (ms): Min: 2.6, Avg: 3.4, Max: 3.7, Diff: 1.1, Sum: 13.6] //Object copy 每个独立线程复制和消亡对象锁花费的时间 [Termination (ms): Min: 0.0, Avg: 0.3, Max: 1.1, Diff: 1.1, Sum: 1.2] //Termination time 当一个工作线程结束了它对特定对象的复制和扫描 [GC Worker Other (ms): Min: 0.0, Avg: 0.0, Max: 0.0, Diff: 0.0, Sum: 0.0] [GC Worker Total (ms): Min: 4.0, Avg: 4.0, Max: 4.0, Diff: 0.0, Sum: 15.9] //GC Worker Total– 所有GC线程所使用的时间 [GC Worker End (ms): Min: 180.6, Avg: 180.6, Max: 180.6, Diff: 0.0] [Code Root Fixup: 0.0 ms] [Clear CT: 0.0 ms] [Other: 0.4 ms] [Choose CSet: 0.0 ms] [Ref Proc: 0.0 ms] [Ref Enq: 0.0 ms] [Free CSet: 0.0 ms] //释放那些已经被收集过的区域,remembered sets所花费的时间 [Eden: 1024.0K(9216.0K)->0.0B(10.0M) Survivors: 1024.0K->0.0B Heap: 9901.8K(20.0M)->9901.8K(20.0M)] [Times: user=0.00 sys=0.00, real=0.
50000+
5万行代码练就真实本领
17年
创办于2008年老牌培训机构
1000+
合作企业
98%
就业率

联系我们

电话咨询

0532-85025005

扫码添加微信