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= -XX:SurvivorRatio= -XX:MaxMetaspaceSize=16m -XX:MaxTenuringThreshold=
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<OutOfMemoryObject> list = new ArrayList<OutOfMemoryObject>();
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 [ 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*1KB的内存 ,大小1MB
} public static void creatHeap(int num) throws Exception {
ArrayList<OutOfMemoryObject> list = new ArrayList<OutOfMemoryObject>();
for (int i = ; i < num; i++) {
list.add(new OutOfMemoryObject());
}
System.gc();
} public static void main(String[] args) throws Exception {
while (true) {
creatHeap();
} }
}
运行结果的,日志分析如下:
[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 [ 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 [ bytes in 0.018 secs]
[GC [ 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: K ( K)][Rescan (parallel) , 0.0002252 secs][weak refs processing, 0.0000048 secs][scrub string table, 0.0001071 secs] [ 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: ]
[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: , Avg: 2.0, Max: , Diff: , Sum: ]
[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: .0K(10.0M)->.0B(.0K) Survivors: .0B->.0K Heap: .6K(20.0M)->.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: ]
[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: , Avg: 0.5, Max: , Diff: , Sum: ]
[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: .0K(.0K)->.0B(10.0M) Survivors: .0K->.0B Heap: .8K(20.0M)->.8K(20.0M)]
[Times: user=0.00 sys=0.00, real=0.00 secs]
Total time for which application threads were stopped: 0.0046272 seconds
[Full GC 9901K->9719K(20M), 0.0030929 secs]
[Eden: .0B(10.0M)->.0B(10.0M) Survivors: .0B->.0B Heap: .8K(20.0M)->.5K(20.0M)]
[Times: user=0.00 sys=0.00, real=0.00 secs]
[Full GC 9719K->9708K(20M), 0.0028649 secs]
[Eden: .0B(10.0M)->.0B(10.0M) Survivors: .0B->.0B Heap: .5K(20.0M)->.5K(20.0M)]
[Times: user=0.00 sys=0.00, real=0.00 secs]
Total time for which application threads were stopped: 0.0060723 seconds
java.lang.OutOfMemoryError: Java heap space
Dumping heap to java_pid11772.hprof ...
Total time for which application threads were stopped: 0.0118791 seconds
Heap dump file created [ bytes in 0.012 secs]
Exception in thread "main" java.lang.OutOfMemoryError: Java heap space
at com.jvm.outofmemory.HeapOutOfMemory$OutOfMemoryObject.<init>(HeapOutOfMemory.java:)
at com.jvm.outofmemory.HeapOutOfMemory.creatHeap(HeapOutOfMemory.java:)
at com.jvm.outofmemory.HeapOutOfMemory.main(HeapOutOfMemory.java:)
Heap
garbage-first heap total 20480K, used 9708K [0x00000000f9a00000, 0x00000000fae00000, 0x00000000fae00000)
region size 1024K, young (1024K), survivors (0K)
compacting perm gen total 20480K, used 2663K
//region 大小1M,一个年轻代region,没有幸存代region
[0x00000000fae00000, 0x00000000fc200000, 0x0000000100000000)
the space 20480K, % used [0x00000000fae00000, 0x00000000fb099cd0, 0x00000000fb099e00, 0x00000000fc200000)
No shared spaces configured.
5.异常信息:
1.java.lang.OutOfMemoryError: PermGen space:永久代被占满,无法为 class 字节码文件分配空间,现在反射,动态代理使用越来越多,字节码插桩技术可以在类加载的前后插入自定义的内容,这些都会增大永久代的占用。
2.java.lang.OutOfMemoryError: Java heap space:内存泄漏
3.java.lang.StackOverflowError:栈空间溢出,通常是栈帧深度过深,如递归调用没有出口,造成死循环。
4.Fatal: Stack size too small:线程栈爆满,通常是方法体代码过多造成,可以通过拆分方法,让方法职责单一避免溢出,也可以通过 -Xss 增大线程栈空间。
6.Java引用类型:
1.强引用:强引用指向的对象在任何时候都不会被系统回收。即使当内存不足时,VM宁愿抛出内存不足的异常,也不会去回收这些对象。
使用场景:我们平常大部分使用的场景都是使用了强引用,比如new创建对象,反射获得一个对象等。如下:
Object obj = new Object();
2.软引用:一个持有软引用的对象,不会被 JVM 很快地回收,JVM 会根据当前堆的使用情况来判断何时回收。(通常用于缓存),如果内存空间不足时,就会回收这些对象的内存。
软引用还可以和一个引用队列进行关联,如果这个软引用的对象被垃圾回收,那么VM就会将这个软引用加入到关联的队列中去。
使用场景: 这种可用于那种有可能会在创建后使用的对象,不过为了内存消耗会选择使用软引用,比如缓存。如下所示:
String cache = "{a=1,b=2,c=3,d=4}";
SoftReference<String> stringSoftReference = new SoftReference<String>(cache);
System.out.println(cache);
3.弱引用:在系统 GC 时,只要发现弱引用,不管系统队空间是否足够,都会将对象进行回收。弱引用和软引用的区别在于,只具有弱引用的对象拥有更短暂的生命周期,在垃圾回收器线程扫描它管辖的内存区域的过程中,一旦发现对象只具有弱引用,不管当前内存空间是否足够,都会回收他的内存。 它比软引用的生命周期更短,和软引用相似,它同样可以和引用队列关联,如果被垃圾回收了,就会加入到这个关联队列中。
使用场景: 弱引用用于生命周期更短的,对内存更敏感的场景中,比如占用内存很大的Map,java api中就提供了WeakHashMap使用,就会是的大Map被及时清理掉。如下:
WeakHashMap<String,Bean> weakHashMap = new WeakHashMap<>();
weakHashMap.put("a",new Bean());
weakHashMap.put("b",new Bean());
weakHashMap.put("c",new Bean());
System.out.println(weakHashMap);
4.虚引用:和没有引用几乎一样。虚引用”形同虚设,与其他几种引用都不同,虚引用并不会决定对象的生命周期,如果一个对象仅持有虚引用的话,那么它就和没有任何的引用一样,在任何时候都可能被垃圾回收器回收。 虚引用必须和引用队列联合使用,引用队列的作用和软弱引用一样。
使用场景: 我觉得他的使用场景应该在判断一个对象是否被垃圾回收了。例子如下:
String name = "a";
ReferenceQueue<String> prq = new ReferenceQueue<>();
PhantomReference<String> nameRf = new PhantomReference<>(name, prq);
System.out.println(prq.poll());
接下来进行一个例子演示Java的引用类型,如下:
package com; import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.WeakHashMap; /**
* Created by cong on 2018/8/5.
*/
public class WeakReference {
// 弱引用
public static Map map = new WeakHashMap();
// 强引用
public static List list = new ArrayList(); public static void main(String[] args) { for (int i = ; i < ; i++) {
Integer j = new Integer(i);
// j强引用
list.add(j);
// 存放在weakHashMap中的key都存在强引用,那么weakHashMap就退化为HashMap,即强引用
map.put(j, new byte[i]);
// 当内存足不足时,会报OutOfMemoryError,因为存在j强引用,没有被使用的map存在的垃圾却无法被清理,造成内存泄漏
} // //把for循环替换成
// for(int i=0;i<999;i++){
// Integer k = new Integer(i);
// map.put(k,new byte[i]);
// //当内存不足时,会被自动回收,weakHashMap 会在内存紧张时,自动释放持有弱引用的数据
// }
} }
二.JVM 问题排查
1.CPU 使用率过高时:
- 通过 top 命令查看服务器的负载情况
- 通过
top+H
查看线程的使用情况 - 通过
jstat -gcutil pid
查看具体线程的 GC 前后内存变化(pid的地方用进程号代替) ps -mp pid -o THREAD,tid,time
查看线程列表printf "%x\n" tid
将 tid 转换成 16 进制格式jstack pid |grep tid -A 60
打印线程(转换后 16 进制格式的数字)tid 的堆栈信息- 通常计算密集型应用产生死锁或者死循环或超时重试导致的线程堆积都会占用 CPU 大量资源,CPU 使用率可能达到 200%
2.线上频繁 Full GC:
1.通过 top+H
查看线程的使用情况
2.jmap -histo:live pid
(进程号) 这个会立即触发 Full GC
3.线上开启了 -XX:+HeapDumpBeforeFullGC
也就是 FullGC 前保存内存快照,JVM 在执行 dump 操作的时是会发生 stop the word,此时所有的用户线程都会暂停运行。为了对外能正常提供服务,可用分布式部署,匹配负载均衡
4.通过 MAT(Memory Analyzer Tool)分析内存快照,把 “Keep unreachable objects” 勾上,否则 MAT 会把堆中不可达的对象去除掉,反而不利于分析, 通过 -XX:+HeapDumpOnOutOfMemoryError
可以让虚拟机在出现内存溢出异常时 Dump 出当前的内存堆转储快照,然后看到 GC 日志 Dumping heap to java_pid11772.hprof ...
,我们可以在工程目录里打开 pid11772.hprof 日志文件(在 Eclipse 应用市场下载 Memory Analyzer Tool 插件并安装,就可以直接打开)
5.查看 Dominator Tree
选项,内存中所有对象都按照内存消耗排名从高到低进行排序
Class Name 是 Java 类的全限定名 Shallow Heap 是对象本身消耗内存大小。
Retained Heap 是对象本身和它所引用的对象的内存大小总和。
Percenttage 是对象消耗占整个堆快照的比率。
6.查看是否使用了大对象,或者长期持有对象的引用,或者大量堆积了全局的本地缓存
电商平台,在做活动促销时,瞬间有大量的客户登录,但是登录页面假死,造成登录失败,在服务器的 log 日志发现大量的超时信息,报线程耗尽,通过 jstack pid
打印进程中线程堆栈信息发现有上百个 thread 不断的重试发送请求,基本定位是大量超时请求重试导致服务器高负荷运行而假死。
我们把服务端提供者登录接口的线程并发数从 10 调成 15,并把消费者重试次数改为 0,请求失败后直接返回,让用户的客户端 Http 进行重试,同时在登录时关闭 Druid 的 SQL 监控功能,避免增加额外资源消耗,最后重新上线,问题修复。
JVM 生产监控的指标有哪些?
关于 GC 的监控,我认为最重要的三点为:
- 各个区的容量。
- Full GC、Young GC 发生的次数。
- 当前系统的内存比、CPU 使用率。
我们以 java/bin/jstat 为例看看相关的参数细节有哪些。
数据列 | 描述 | 支持的jstat 选项 |
---|---|---|
S0C | Survivor0的当前容量 | -gc -gccapacity -gcnew -gcnewcapacity |
S1C | S1的当前容量 | -gc -gccapacity -gcnew -gcnewcapacity |
S0U | S0的使用量 | -gc-gcnew |
S1U | S1的使用量 | -gc-gcnew |
EC | Eden区的当前容量 | -gc -gccapacity -gcnew -gcnewcapacity |
EU | Eden区的使用量 | -gc -gcnew |
OC | old区的当前容量 | -gc -gccapacity -gcnew -gcnewcapacity |
OU | old区的使用量 | -gc-gcnew |
PC | 方法区的当前容量 | -gc-gccapacity -gcold -gcoldcapacity -gcpermcapacity |
PU | 方法区的使用量 | -gc -gcold |
YGC | Young GC次数 | -gc -gccapacity -gcnew -gcnewcapacity -gcold -gcoldcapacity -gcpermcapacity -gcutil -gccause |
YGCT | Young GC累积耗时 | -gc -gcnew -gcutil -gccause |
FGC | Full GC次数 | -gc -gccapacity -gcnew -gcnewcapacity -gcold -gcoldcapacity -gcpermcapacity -gcutil -gccause |
FGCT | Full GC累积耗时 | -gc-gcold -gcoldcapacity -gcpermcapacity -gcutil -gccause |
GCT | GC总的累积耗时 | -gc -gcold -gcoldcapacity -gccapacity -gcpermcapacity -gcutil -gccause |
NGCMN | 新生代最小容量 | -gccapacity -gcnewcapacity |
NGCMX | 新生代最大容量 | -gccapacity -gcnewcapacity |
NGC | 新生代当前容量 | -gccapacity -gcnewcapacity |
OGCMN | 老年代最小容量 | -gccapacity -gcoldcapacity |
OGCMX | 老年代最大容量 | -gccapacity -gcoldcapacity |
OGC | 老年代当前容量 | -gccapacity -gcoldcapacity |
PGCMN | 方法区最小容量 | -gccapacity -gcpermcapacity |
PGCMX | 方法区最大容量 | -gccapacity -gcpermcapacity |
PGC | 方法区当前容量 | -gccapacity -gcpermcapacity |
PC | 方法区的当前容量 | -gccapacity -gcpermcapacity |
PU | 方法区使用量 | -gccapacity -gcold |
LGCC | 上一次GC发生的原因 | -gccause |
GCC | 当前GC发生的原因 | -gccause |
TT | 存活阀值,如果对象在新生代移动次数超过此阀值,则会被移到老年代 | -gcnew |
MTT | 最大存活阀值,如果对象在新生代移动次数超过此阀值,则会被移到老年代 | -gcnew |
DSS | survivor区的理想容量 | -gcnew |
轻松应对 JVM 的面试和实际工作。
针对面试,建议读者保持两点:
- 思路清晰,说的是通的。建议读者可以根据作者整理的内容弄个简单的思路导图。
- 面要全,有一到两个点要细。概况起来了就三点,如下。
- 不同视角的内存模型、
- GC 对应的4种算法。
- GC 对应的5种收集器。
针对于工作,希望大家了解以下几点:
- 一定要知道 JDK 版本,JVM 默认参数有哪些?
- 我可以设置哪些?哪些是必须设置的?
- 可以直接参看很多容器,如 Tomcat、Jetty、Docker 的默认 JVM 参数。
JVM笔记10-性能优化之高级特性的更多相关文章
- 【转】【技术博客】Spark性能优化指南——高级篇
http://mp.weixin.qq.com/s?__biz=MjM5NjQ5MTI5OA==&mid=2651745207&idx=1&sn=3d70d59cede236e ...
- Spark性能优化指南——高级篇
本文转载自:https://tech.meituan.com/spark-tuning-pro.html 美团技术点评团队) Spark性能优化指南——高级篇 李雪蕤 ·2016-05-12 14:4 ...
- Spark Tungsten揭秘 Day1 jvm下的性能优化
Spark Tungsten揭秘 Day1 jvm下的性能优化 今天开始谈下Tungsten,首先我们需要了解下其背后是符合了什么样的规律. jvm对分布式天生支持 整个Spark分布式系统是建立在分 ...
- Android App性能优化笔记之一:性能优化是什么及为什么?
By Long Luo 周星驰的电影<功夫>里面借火云邪神之口说出了一句至理名言:“天下武功,唯快不破”. 在移动互联网时代,同样如此,留给一个公司的窗口往往只有很短的时间,如何把握住 ...
- Spark性能优化指南-高级篇(spark shuffle)
Spark性能优化指南-高级篇(spark shuffle) 非常好的讲解
- 【转载】Spark性能优化指南——高级篇
前言 数据倾斜调优 调优概述 数据倾斜发生时的现象 数据倾斜发生的原理 如何定位导致数据倾斜的代码 查看导致数据倾斜的key的数据分布情况 数据倾斜的解决方案 解决方案一:使用Hive ETL预处理数 ...
- Spark性能优化指南——高级篇(转载)
前言 继基础篇讲解了每个Spark开发人员都必须熟知的开发调优与资源调优之后,本文作为<Spark性能优化指南>的高级篇,将深入分析数据倾斜调优与shuffle调优,以解决更加棘手的性能问 ...
- Spark性能优化指南-高级篇
转自https://tech.meituan.com/spark-tuning-pro.html,感谢原作者的贡献 前言 继基础篇讲解了每个Spark开发人员都必须熟知的开发调优与资源调优之后,本文作 ...
- [性能优化] perf 高级用法:完整记录程序性能指标,并按照时间段对程序进行有针对性的性能分析
如题: 假设你已经熟悉了基本用法,知道perf是干嘛的,以及会用 perf top [性能优化] perf 背景:目标程序在运行的某时间段内会出现性能下降,需要了解这个时间内,程序发生了什么. 方法: ...
随机推荐
- Python开发——数据类型【列表】
列表的定义 中括号[]内以逗号分隔开,按照索引,存放各种数据类型,每个位置代表一个元素 list_t = ['张三','Lucy',123] print(list_t) # ['张三', 'Lucy' ...
- python02 运算符,基本数据类型,整型,字符串
1.python开发IDE pycharm,python编写工具,, #专业版 #不需要汉化 注册码问题解决 https://www.cnblogs.com/evlon/p/4934705.html整 ...
- tian_lie
后台托管:nohup ./re_start_job.sh kg_fk_etl >>log.log 2>&1 & 查看进程:ps -ef|grep kg_fk_etl ...
- 《C#从现象到本质》读书笔记(三)第3章C#类型基础(下)
<C#从现象到本质>读书笔记第3章C#类型基础(下) 常量以关键字const修饰.C#支持静态字段(类型字段)和实例字段. 无参属性的get方法不支持参数,而有参属性的get方法支持传入一 ...
- 数据库-mysql语句-查-WEB服务器
(1)MySQL中的查询 (2)WEB服务器 Order: 订单.排序 Set:设置.集合 Table:表.桌子 1.MySQL中的简单查询 —— 查询结果的排序 示例:查询出所有员工信息,要求按工 ...
- Python Day 3
阅读目录: 内容回顾: 变量(标识符)的命名规范: 常量: 格式化输入\输出: 注释: 基本数据类型: 运算符: ##内容回顾 1.语言的分类: -- 机器语言:直接编写0,1指令,直接能被硬件执行. ...
- 手动上传图片到nginx下可访问,程序上传后访问图片报403
1. 首先查看文件权限 2. 初步确定是服务器权限问题 2.1 解决方案一:更改文件权限 2.2 解决方案二:修改nginx运行用户 1. 首先查看文件权限 #指令如下 ls -l 2. 初步确定是服 ...
- linux mysql 5.7.25 安裝
1.下载 https://cdn.mysql.com//Downloads/MySQL-5.7/mysql-5.7.25-linux-glibc2.12-x86_64.tar.gz 2.解压 tar ...
- POI SXSSFWorkbook 读取模板 存在公式解决
package com.baoqilai.base.service.export; import java.io.File; import java.io.FileInputStream; impor ...
- 滑块视图容器 swiper
属性名 类型 默认值 说明 indicator-dots Boolean false 是否显示面板指示点 autoplay Boolean false 是否自动切换 current Number 0 ...