一、内存
(1)输出GC日志
-XX:+PrintGCTimeStamps -XX:+PrintGCDetails -Xloggc:<filename>
-XX:+PrintGCTimeStamps输出从虚拟机启动后到gc发生时的经历的时间(秒)。-XX:+PrintGCDetails输出gc统计信息,-Xloggc:<filename>,gc统计信息保存文件。
例子:(-XX:+UseParallelOldGC or -XX:+UseParallelGC)
0.064: [GC (Allocation Failure) [PSYoungGen: 512K->480K(1024K)] 512K->572K(3584K), 0.0016558 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
0.064,发生gc的时间标签,单位是s;
GC标签,表明当前GC是一个新生代GC(即minor gc);
PSYoungGen,多线程的吞吐量优先的新生代垃圾收集器;
512K->480K(1024),512K是垃圾收集之前新生代内存占用大小,480K是垃圾收集之后,新生代内存占用大小。(1024K)是新生代的大小包括eden,survivor区;
512K->572K(3584K),表示整个java堆(新生代+老年代)在gc前后的内存占用;
0.0016558 secs,垃圾收集用时;
-XX:+PrintGCDateStamps输出具体格式的时间,YYYY-MM-DDTHH-MM-SS
(2)调整内存
HotSpot VM有三个主要的区域,新生代、老年代、永久代。新生代保存存活时间较短的对象,老年代保存存活时间较长的对象,永久代保存类元数据和静态变量等。-Xms 和-Xmx指定java堆的初始大小,和最大容量。为保证运行程序高吞吐量和低延迟,-Xms和-Xmx的值设置成一样,禁止动态扩展,动态扩展会引起Full GC。
-XX:NewSize=<n>[g|m|k]
-XX:MaxNewSize=<n>[g|m|k]
-Xmn<n>[g|m|k]
-Xmn设置的值,既是新生代的初始值,也是新生代的最大值,设置此值后新生代不动态扩展,因此-Xmn用在-Xms和-Xmx值一样大的情形下。
老年代一般都是隐式设置,初始化老年代大小是-Xms 减去 -XX:NewSize,最大老年代是-Xmx减-XX:MaxNewSize,当-Xms和-Xmx一样大时,老年代是-Xmx减去-Xmn的值。
-XX:PermSize=<n>[g|m|k]
-XX:MaxPermSize=<n>[g|m|k]
设置永久代和最大永久代。若为追求更好的性能,需把初始永久代和最大永久代设置成相同的值,避免动态扩展带来的Full GC。
当新生代、老年代、永久代已经存满了,此时,新的请求需要分配内存,就会发生gc(垃圾收集)。若是新生代不够了,就触发minor GC。当老年代装不下已经老化的对象,永久代被占满时会触发Full GC。
-XX:-ScavengeBeforeGC
配置-XX:-ScanvengeBeforeGC时,在Full GC期间不会进行Minor GC。
jmap -histo:live pid
此命令可能触发一次Full GC。
-Xms和-Xmx应被设置为(Full GC后)存活的老年代空间的3~4倍;新生代-Xmn应设置为存活老年代空间的1~1.5倍,老年代应设置为存活老年代的2~3倍;-XX:PermSize和-XX:MaxPermSize应设置为永久代存活空间的1.2~1.5倍。
例子如下:
46.620: [Full GC (Heap Inspection Initiated GC) [PSYoungGen: 273K->0K(1536K)] [ParOldGen: 1640K->1369K(2048K)] 1914K->1369K(3584K), [Metaspace: 4944K->4940K(1056768K)], 0.0071578 secs] [Times: user=0.00 sys=0.00, real=0.01 secs]
1369K * 4 = 5476k,-Xms5476k,-Xmx5476k
1369 * 1.5 = 2053.5 , -Xmn2054k
1369 * 2 = 2738k,1369 * 3 = 4107k
4940 * 1.5 = 7410k,-XX:PermSize=7410k,-XX:MaxPermSize=7410k
二、调优吞吐量
(1)Throughput吞吐量
-XX:+UseParallelOldGC
-XX:+UseParallelGC
-XX:-UseAdaptiveSizePolicy
-XX:-UseAdaptiveSizePolicy禁用自适应大小调整。只有Throughput收集器,支持自适应大小调整。
-XX:SurvivorRatio=8
-XX:+PrintAdaptiveSizePolicy
-XX:+PrintAdaptiveSizePolicy可以生成更详细的Survivor空间占用日志,无论Survivor空间溢出,还是对象从新生代提升进入老年代统统在其中。
[PSYoungGen: 1525K->418K(1536K)] 2749K->2013K(5632K), 0.0007124 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
2020-04-17T16:23:54.683+0800: 23.084: [GC (Heap Inspection Initiated GC) AdaptiveSizePolicy::update_averages: survived: 313224 promoted: 98304 overflow: false
survived:313224是To Survivor空间中存活对象的大小;promoted:98304是由新生代提升到老年代对象的大小;overflow:false表示是否Survivor空间的对象溢出到老年代空间(to空间小,装不下eden和from去存活对象)。
(2)Survivor空间调优
调整survivor大小并不仅是将Survivor空间大小设置成某个值,或者调整为稍大于从GC日志中获得的最大存活对象的值那么简单。在增大Survivor空间时,应该保持Eden空间大小的恒定不变。按Survivor空间的增量,增大新生代空间,同时维持老年代空间大小不变。HotSpot虚拟机Minor GC之后,目标survivor空间的占用的默认值是50%。(但是,若内存占用要求或系统可用内存不允许这样设置,需确保调整后的老年代空间远大于活跃数据大小。推荐准则,老年代空间至少应该是活跃数据大小的1.5倍)
overflow溢出案例,下面列出了jvm配置,和gc日志
-Xms6m -Xmx6m -Xmn2m -XX:+PrintGCDateStamps -XX:+PrintGCDetails -XX:-UseAdaptiveSizePolicy -XX:+PrintAdaptiveSizePolicy -Xl oggc:./gc.log
2020-04-17T17:31:31.861+0800: 0.408: [GC (Allocation Failure) AdaptiveSizePolicy::update_averages: survived: 499744 promoted: 173728 overflow: true
[PSYoungGen: 1512K->488K(1536K)] 2785K->1931K(5632K), 0.0007031 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
499744 / (50%) = 999488字节,大约1m,jvm调整如下(当然实际情况下SurvivorRatio不会是1):
-Xms7m -Xmx7m -Xmn3m -XX:SurvivorRatio=1 -XX:+PrintGCDateStamps -XX:+PrintGCDetails -XX:-UseAdaptiveSizePolicy -XX:+PrintAdaptiveSizePolicy -Xloggc:./gc.log
-XX:TargetSurvivorRatio=<n>
目标Survivor空间的占用百分比,默认值是50%,如果应用程序的对象分配有大幅波动,将目标Survivor空间占用大小设置成大于50会导致Survivor空间溢出。
(3)调优并行垃圾收集线程
-XX:ParallelGCThreads=<n>
并行垃圾收集器使用的线程数,应依据系统上运行的应用程序数和底层硬件平台进行相应的调优。默认情况下并行垃圾收集线程数等于Java API Runtime.availableProcessors()的返回值(如果返回值小于等于8),否则其等于Runtime.avaliableProcessors()返回值的5/8。多个应用程序运行于同一系统时,设置并行垃圾收集线程的一个通用原则是用虚拟处理器的数目(Runtime.availableProcessors()的返回值)除以该系统上运行的应用程序数(假设应用程序的负荷和堆大小的情况相差不大)。如果负荷及java堆大小差异很大,那么为每个java应用设置不同权重,并据此设置并行线程数。
三、下一步
如果调优进行到这一步,仍无法达到应用程序的吞吐量要求,那就需要回顾应用程序的性能要求,修改应用程序、或者改变JVM的部署模式。一旦选择了一个方式,就可以继续新一轮的调优。