Contenu connexe Similaire à Sun jdk-1.6-gc (20) Sun jdk-1.6-gc1. Sun JDK 1.6 GC
(Garbage Collector)
http://bluedavy.com
2010-05-13 V0.2
2010-05-19 V0.5
2010-06-01 V0.8
ppt中未特别强调的JVM均指Sun JDK 1.6.0
6. 内存结构
-Xss
局部变量区 本地方法栈
PC寄 操作数栈 -XX:PermSize –
存器 栈帧
JVM方法区 XX:MaxPermSize
JVM方法栈 JVM堆 -Xms -Xmx
备注:在Hotspot中本地方法栈和JVM方法栈是同一个,因此也可用-Xss控制
10. JVM堆:分代
-Xmn New Generation
Eden S0 S1 Old Generation
-XX:SurvivorRatio
备注:通常将对新生代进行的回收称为Minor GC;对旧生代进行的回收称为Major GC,但由于
Major GC除并发GC外均需对整个堆进行扫描和回收,因此又称为Full GC。
12. 新生代可用GC
串行GC 并行回收GC
并行GC
(Serial (Parallel
(ParNew)
Copying) Scavenge)
我该用哪个呢?
15. 新生代可用GC—串行
当eden space空间不足时触发。
public class SerialGCDemo{
public static void main(String[] args) throws Exception{
byte[] bytes=new byte[1024*1024*2];
byte[] bytes2=new byte[1024*1024*2];
byte[] bytes3=new byte[1024*1024*2];
System.out.println(“step 1");
byte[] bytes4=new byte[1024*1024*2];
Thread.sleep(3000);
System.out.println(“step 2");
byte[] bytes5=new byte[1024*1024*2];
byte[] bytes6=new byte[1024*1024*2];
System.out.println(“step 3");
byte[] bytes7=new byte[1024*1024*2];
Thread.sleep(3000);
}
}
-Xms20M –Xmx20M –Xmn10M –XX:+UseSerialGC
17. 新生代可用GC—串行
public class SerialGCDemo{
public static void main(String[] args) throws Exception{
byte[] bytes=new byte[1024*1024*2];
byte[] bytes2=new byte[1024*1024*2];
byte[] bytes3=new byte[1024*1024*2];
System.out.println("step 1");
bytes=null;
byte[] bytes4=new byte[1024*1024*2];
Thread.sleep(3000);
System.out.println("step 2");
byte[] bytes5=new byte[1024*1024*2];
byte[] bytes6=new byte[1024*1024*2];
bytes4=null;
bytes5=null;
bytes6=null;
System.out.println("step 3");
byte[] bytes7=new byte[1024*1024*2];
Thread.sleep(3000);
}
}
-Xms20M –Xmx20M –Xmn10M –XX:+UseSerialGC
-Xms20M –Xmx20M –Xmn10M -XX:-HandlePromotionFailure –XX:+UseSerialGC
20. 新生代可用GC—串行
public class SerialGCThreshold{ -Xms20M –Xmx20M –
public static void main(String[] args) throws Exception{ Xmn10M –
SerialGCMemoryObject object1=new SerialGCMemoryObject(1);
SerialGCMemoryObject object2=new SerialGCMemoryObject(8);
XX:+UseSerialGC
SerialGCMemoryObject object3=new SerialGCMemoryObject(8);
SerialGCMemoryObject object4=new SerialGCMemoryObject(8);
object2=null;
object3=null;
-Xms20M –Xmx20M –
SerialGCMemoryObject object5=new SerialGCMemoryObject(8); Xmn10M –
Thread.sleep(4000); XX:+UseSerialGC
object2=new SerialGCMemoryObject(8);
object3=new SerialGCMemoryObject(8); -
object2=null; XX:MaxTenuringThres
object3=null;
object5=null;
hold=1
SerialGCMemoryObject object6=new SerialGCMemoryObject(8);
Thread.sleep(5000);
}
}
class SerialGCMemoryObject{
private byte[] bytes=null;
public SerialGCMemoryObject(int multi){
bytes=new byte[1024*256*multi];
}
}
22. 新生代可用GC—串行
上面示例中object1在第二次minor gc时直接转入了old,在于Serial GC的
这个规则:
每次Minor GC后会重新计算TenuringThreshold
(第一次以MaxTenuringThreshold为准)
计算的规则为:
累积每个age中的字节,当这个累计值 > To Space的一半时,对比此时的age和
MaxTenuringThreshold,取其中更小的值。
可通过PrintTenuringDistribution来查看下次minor gc时的TenuringThreshold
值:Desired survivor size 524288 bytes, new threshold 1 (max 15),其中
的new threshold 1即为新的TenuringThreshold的值。
例如在上面的例子中:
当第一次Minor GC结束时,遍历age table,当累积age 1的字节后,发现此时所
占用的字节数 > To Space的一半,因此将TenuringThreshold赋值为1,下次
Minor GC时即把age超过1的对象全部转入old。
25. 新生代可用GC—ParNew
[GC [ParNew: 11509K->1152K(14336K), 0.0129150 secs] 11509K-
>1152K(38912K),
0.0131890 secs] [Times: user=0.05 sys=0.02, real=0.02 secs]
如启动参数上设置了-XX:+UseAdaptiveSizePolicy,则会输出
[GC [ASParNew: 7495K->120K(9216K), 0.0403410 secs] 7495K-
>7294K(19456K), 0.0406480 secs] [Times: user=0.06
sys=0.15, real=0.04 secs]
27. 新生代可用GC—PS
大多数情况下,会在TLAB或eden上分配。
如下一段代码:
public class PSGCDemo{
public static void main(String[] args) throws Exception{
byte[] bytes=new byte[1024*1024*2];
byte[] bytes2=new byte[1024*1024*2];
byte[] bytes3=new byte[1024*1024*2];
Thread.sleep(3000);
byte[] bytes4=new byte[1024*1024*4];
Thread.sleep(3000);
}
}
-Xms20M –Xmx20M –Xmn10M –XX:SurvivorRatio=8 –XX:+UseParallelGC
29. 新生代可用GC—PS
eden space分配不下,且需要分配的对象大小未超过eden space的一半或old区分配失败,
触发回收;
public class PSGCDemo{
public static void main(String[] args) throws Exception{
byte[] bytes=new byte[1024*1024*2];
byte[] bytes2=new byte[1024*1024*2];
byte[] bytes3=new byte[1024*1024*2];
System.out.println(“step 1");
byte[] bytes4=new byte[1024*1024*2];
Thread.sleep(3000);
System.out.println(“step 2");
byte[] bytes5=new byte[1024*1024*2];
byte[] bytes6=new byte[1024*1024*2];
System.out.println(“step 3");
byte[] bytes7=new byte[1024*1024*2];
Thread.sleep(3000);
}
}
-Xms20M –Xmx20M –Xmn10M –XX:SurvivorRatio=8 –XX:+UseParallelGC
-XX:+PrintGCDetails –XX:verbose:gc
31. 新生代可用GC—PS
新生代对象晋升到旧生代的规则
1、经历多次minor gc仍存活的对象,可通过以下参数来控制:
AlwaysTenure,默认false,表示只要minor GC时存活,就晋升到旧生代;
NeverTenure,默认false,表示永不晋升到旧生代;
上面两个都没设置的情况下,如UseAdaptiveSizePolicy,启动时以
InitialTenuringThreshold值作为存活次数的阈值,在每次ps gc后会动态调整
如不使用UseAdaptiveSizePolicy,则以MaxTenuringThreshold为准。
2、to space放不下的,直接放入旧生代;
34. 旧生代可用的GC
串行GC 并行 MS GC 并行 Compacting GC 并发GC
(Serial MSC) (Parallel MSC) (Parallel Compacting) (CMS)
我该用哪个呢?
37. 旧生代可用GC—串行
[Full GC [Tenured: 9216K->4210K(10240K), 0.0066570 secs]
16584K->4210K(19456K), [Perm : 1692K-
>1692K(16384K)], 0.0067070 secs]
[Times: user=0.00 sys=0.00, real=0.01 secs]
40. 旧生代可用GC—并行MSC
[Full GC [PSYoungGen: 1208K->0K(8960K)] [PSOldGen: 6144K-
>7282K(10240K)] 7352K->7282K(19200K) [PSPermGen: 1686K-
>1686K(16384K)], 0.0165880 secs] [Times: user=0.01
sys=0.01, real=0.02 secs]
43. 旧生代可用GC—并行Compacting
[Full GC [PSYoungGen: 1224K->0K(8960K)] [ParOldGen: 6144K-
>7282K(10240K)] 7368K->7282K(19200K) [PSPermGen: 1686K-
>1685K(16384K)], 0.0223510 secs] [Times: user=0.02
sys=0.06, real=0.03 secs]
45. 旧生代可用GC—并发
触发机制
1、当旧生代空间使用到一定比率时触发;
JDK V 1.6中默认为92%,可通过PrintCMSInitiationStatistics(此参数在V
1.5中不能用)来查看这个值到底是多少;
可通过CMSInitiatingOccupancyFraction来强制指定,默认值并不是赋值在
了这个值上,是根据如下公式计算出来的:
((100 - MinHeapFreeRatio) +(double)(CMSTriggerRatio * MinHeapFreeRatio) / 100.0)/ 100.0;
MinHeapFreeRatio默认值: 40 CMSTriggerRatio默认值: 80
2、当perm gen采用CMS收集且空间使用到一定比率时触发;
perm gen采用CMS收集需设置:-XX:+CMSClassUnloadingEnabled
JDK V 1.6中默认为92%;
可通过CMSInitiatingPermOccupancyFraction来强制指定,同样,它是根据
如下公式计算出来的:
((100 - MinHeapFreeRatio) +(double)(CMSTriggerPermRatio* MinHeapFreeRatio) / 100.0)/ 100.0;
MinHeapFreeRatio默认值: 40 CMSTriggerPermRatio默认值: 80
47. 旧生代可用GC—并发
public class CMSGCOccur{
public static void main(String[] args) throws Exception{
byte[] bytes=new byte[1024*1024*2];
byte[] bytes1=new byte[1024*1024*2];
byte[] bytes2=new byte[1024*1024*2];
byte[] bytes3=new byte[1024*1024*1];
byte[] bytes4=new byte[1024*1024*2];
Thread.sleep(5000);
}
}
-Xms20M –Xmx20M –Xmn10M -XX:+UseConcMarkSweepGC -
XX:+UseCMSInitiatingOccupancyOnly -XX:+PrintGCDetails
-Xms20M –Xmx20M –Xmn10M -XX:+UseConcMarkSweepGC -
XX:+PrintGCDetails
48. 旧生代可用GC—并发
1. Promotion Failed
minor GC了,to space空间不够,往old跑,old也满了,so..
解决方法:增大to space,增大old,或降低cms gc触发时机
2. Concurrent mode failure
old要分配内存了,但old空间不够,此时cms gc正在进行,so..
解决方法:增大old,降低cms gc触发的old所占比率。
在这两种情况下,为了安全,JVM转为触发Full GC。
49. 旧生代可用GC—并发
[GC [1 CMS-initial-mark: 13433K(20480K)] 14465K(29696K), 0.0001830 secs]
[Times: user=0.00 sys=0.00, real=0.00 secs]
[CMS-concurrent-mark: 0.004/0.004 secs] [Times: user=0.01 sys=0.00, real=0.01 secs]
[CMS-concurrent-preclean: 0.000/0.000 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
CMS: abort preclean due to time [CMS-concurrent-abortable-preclean: 0.007/5.042 secs]
[Times: user=0.00 sys=0.00, real=5.04 secs]
[GC[YG occupancy: 3300 K (9216 K)][Rescan (parallel) , 0.0002740 secs]
[weak refs processing, 0.0000090 secs]
[1 CMS-remark: 13433K(20480K)] 16734K(29696K), 0.0003710 secs]
[Times: user=0.00 sys=0.00, real=0.00 secs]
[CMS-concurrent-sweep: 0.000/0.000 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
[CMS-concurrent-reset: 0.000/0.000 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
当在启动参数上设置了-XX:+UseAdaptiveSizePolicy后,上面的日志中的CMS会变为ASCMS
CMS GC Log解读
50. GC—默认组合
新生代GC方式 旧生代和持久代GC方式
Client 串行GC 串行GC
Server 并行回收GC 并行 MSC GC(JDK 5.0 Update 6以后)
51. GC—组合使用
新生代GC方式 旧生代和持久代GC方式
-XX:+UseSerialGC 串行GC 串行GC
-XX:+UseParallelGC PS GC 并行MSC GC
-XX:+UseConcMarkSweepGC ParNew GC 并发GC
当出现concurrent Mode
failure时采用串行GC
-XX:+UseParNewGC 并行GC 串行GC
-XX:+UseParallelOldGC PS GC 并行Compacting GC
-XX:+UseConcMarkSweepGC 串行GC 并发GC
-XX:-UseParNewGC 当出现Concurrent Mode
failure或promotion failed
时则采用串行GC
不支持的组合方式 1、-XX:+UseParNewGC –XX:+UseParallelOldGC
2、-XX:+UseParNewGC –XX:+UseSerialGC
52. GC方式 常用参数(-Xms –Xmx –Xmn –XX:PermSize –XX:MaxPermSize)
新生代可用GC 串行GC -XX:SurvivorRatio,默认为8,代表eden:survivor;
-XX:MaxTenuringThreshold,默认为15,代表对象在新生代经历多少次minor gc后才晋升到
旧生代;
PS GC -XX:InitialSurvivorRatio,默认为8,代表new gen:survivor;
-XX:SurvivorRatio,默认值对于PS GC无效,但仍然可设置,代表eden:survivor;
-XX:-UseAdaptiveSizePolicy , 不 允 许 PS GC 动 态 调 整 eden 、 s0 、 s1 的 大 小 , 此 时 -
XX:MaxTenuringThreshold也可使用;
-XX:ParallelGCThreads,设置并行GC的线程数。
ParNew GC 同串行。
旧生代和持久代可用GC 串行GC 无特殊参数。
并行GC -XX:ParallelGCThreads,设置并行GC的线程数。
(包括MSC、 -XX:+ScavengeBeforeFullGC,Full GC前触发Minor GC
Compacting)
并发GC -XX:ParallelCMSThreads,设置并发CMS GC时的线程数;
-XX:CMSInitiatingOccupancyFraction,当旧生代使用比率占到多少百分比时触发CMS GC;
-XX:+UseCMSInitiatingOccupancyOnly,默认为false,代表允许hotspot根据成本来决定什么
时候执行CMS GC;
-XX:+UseCMSCompactAtFullCollection,当Full GC时执行压缩;
-XX:CMSMaxAbortablePrecleanTime=5000,设置preclean步骤的超时时间,单位为毫秒;
-XX:+CMSClassUnloadingEnabled,Perm Gen采用CMS GC回收。
55. OOM(一些代码造成OOM的例子)
Java Heap OOM产生的原因是在多次gc后仍然分配不了,具体策略取决于这三个
参数:
-XX:+UseGCOverheadLimit -XX:GCTimeLimit=98 –XX:GCHeapFreeLimit=2
1、OOM的前兆通常体现在每次Full GC后旧生代的消耗呈不断上涨趋势;
查看方法:jstat –gcutil [pid] [intervel] [count]
2、解决方法
dump多次Full GC后的内存消耗状况,方法:
jmap –dump:format=b,file=[filename] [pid]
dump下来的文件可用MAT进行分析,简单视图分析:MAT Top Consumers
或在启动参数上增加:-XX:+HeapDumpOnOutOfMemoryError,当OOM
时会在工作路径(或通过-XX:HeapDumpPath来指定路径)下生成
java_[pid].hprof文件。
还有Native Heap造成的OOM,堆外内存使用过多。
56. GC监测
1、jstat –gcutil [pid] [intervel] [count]
2、-verbose:gc // 可以辅助输出一些详细的GC信息
-XX:+PrintGCDetails // 输出GC详细信息
-XX:+PrintGCApplicationStoppedTime // 输出GC造成应用暂停的时间
-XX:+PrintGCDateStamps // GC发生的时间信息
-XX:+PrintHeapAtGC // 在GC前后输出堆中各个区域的大小
-Xloggc:[file] // 将GC信息输出到单独的文件中
gc的日志拿下来后可使用GCLogViewer或gchisto进行分析。
3、图形化的情况下可直接用jvisualvm进行分析。
59. case 1 – 场景
4 cpu,os: linux 2.6.18 32 bit
启动参数
◦ -Xms1536M –Xmx1536M –Xmn500M
系统响应速度大概为100ms;
当系统QPS增长到40时,机器每隔5秒就执行一
次minor gc,每隔3分钟就执行一次full gc,并
且很快就一直full了;
每次Full gc后旧生代大概会消耗400M,有点多
了。
60. case 1 – 目标和方法
减少Full GC次数,以避免由于GC造成频繁的长
暂停,从而导致难以支撑高并发量。
方法
◦ 降低响应时间或请求次数,这个需要重构,比较麻烦;
◦ 减少旧生代内存的消耗,比较靠谱;
◦ 减少每次请求的内存的消耗,貌似比较靠谱;
◦ 降低GC造成的应用暂停的时间。
61. case 1 – tuning
减少旧生代内存的消耗
◦ jmap dump;
◦ 发现除了一些确实需要在旧生代中消耗的内存外,还有
点诡异现象;
可以肯定的是这里面的线程大部分是没有在处理任务的;
于是根据MAT查找到底谁消耗掉了这些内存;
◦ 发现是由于有一个地方使用到了ThreadLocal,但在使用完毕后
没有去将ThreadLocal.set(null)。
62. case 1 – tuning
在做完上面的tuning后,旧生代的内存使用下降
了大概200M,效果是full gc的频率稍微拖长了
一点,但仍然不理想,于是旧生代部分无法继续
优化了;
想减少每次请求所分配的内存,碰到的巨大问
题:
◦ 怎么才知道呢?貌似没办法
◦ 想了一些方法,放弃了。
63. case 1 – tuning
降低GC所造成的长暂停
◦ 采用CMS GC
◦ QPS只能提升到50…
◦ 于是放弃,而且还有和jmap –heap的冲突。
64. case 1 – tuning
终极必杀技
◦ 降低系统响应时间
QPS终于能支撑到90…
65. case 2 – 场景
4 cpu,os: linux 2.6.18 32 bit
启动参数
◦ -server -Xms1536m -Xmx1536m –Xmn700m
在系统运行到67919.837秒时发生了一次Full GC,日志信息
如下:
67919.817: [GC [PSYoungGen: 588706K->70592K(616832K)]
1408209K->906379K(1472896K),
0.0197090 secs] [Times: user=0.06 sys=0.00,
real=0.02 secs]
67919.837: [Full GC [PSYoungGen: 70592K->0K(616832K)]
[PSOldGen: 835787K->375316K(856064K)]
906379K->375316K(1472896K)
[PSPermGen: 64826K->64826K(98304K)],
0.5478600 secs]
[Times: user=0.55 sys=0.00, real=0.55 secs]
67. case 2 – 场景
在68132.893时又发生了一次Full GC,日志信息如下:
◦ 68132.862: [GC [PSYoungGen: 594736K-
>63715K(609920K)] 1401225K->891090K(1465984K),
0.0309810 secs] [Times: user=0.06
sys=0.01, real=0.04 secs]
◦ 68132.893: [Full GC [PSYoungGen: 63715K-
>0K(609920K)]
[PSOldGen: 827375K->368026K(856064K)]
891090K->368026K(1465984K)
[PSPermGen: 64869K-
>64690K(98304K)], 0.5341070 secs]
[Times: user=0.53 sys=0.00, real=0.53
secs]
之后的时间的GC基本也在重复上述过程。
68. case 2 – 目标和方法
目标
◦ 降低full gc的频率,以及gc所造成的应用暂停;
◦ 如果在达到上面目标的情况下,还能降低minor gc的
频率以及所造成的应用暂停时间就好了。
方法
◦ 降低响应时间或请求次数,比较麻烦;
◦ 减少每次请求所需分配的内存,貌似比较麻烦;
◦ 减少每次minor gc晋升到old的对象,比较靠谱。
69. case 2 – tuning
减少每次minor gc晋升到old的对象
◦ 调大new,在当前的参数下不好操作;
◦ 调大Survivor,根据目前的状况,是可选的方法;
◦ 调大TenuringThreshold,根据目前的状况,这不是
关键点。
70. case 2 – tuning
调大Survivor
◦ 当前为PS GC方式,Survivor space会被动态调整,有些
时候会调整的很小,所以导致了经常有对象直接跳到了
old;
◦ 于是不让动态调整了,-XX:-UseAdaptiveSizePolicy
◦ 计算Survivor Space需要的大小,简单的计算了下
看目前的to space的大小,然后minor gc后晋升到old的,
old+to space的大小作为需要的大小;
统计多次后做平均;
◦ 于是调整…
◦ 继续观察,并做微调,保证高峰期以及一定的冗余。
◦ 做过这个调整后,效果很明显,minor gc更频繁了些,但
full的频率推迟到了至少两小时一次。
71. case 2 – tuning
上面的tuning达到了降低full gc的目标,但整体GC
所造成的响应时间下降的仍然不够多,大概只下降了
10%;
于是保持Survivor space,同时将GC方式切换为
CMS GC。
◦ -Xms1536m -Xmx1536m -Xmn700m -
XX:SurvivorRatio=7 -XX:+UseConcMarkSweepGC -
XX:+UseCMSCompactAtFullCollection -
XX:CMSMaxAbortablePrecleanTime=1000 -
XX:+CMSClassUnloadingEnabled -
XX:+UseCMSInitiatingOccupancyOnly -
XX:+DisableExplicitGC
72. GC Tuning
1、评估现状
5、细微调整 2、设定目标
4、衡量调优 3、尝试调优
73. GC Tuning—衡量现状
1. 衡量工具
-XX:+PrintGCDetails –XX:+PrintGCApplicationStoppedTime
-Xloggc: {文件名} –XX:+PrintGCTimeStamps
jmap(由于每个版本jvm的默认值可能会有改变,建议还是用jmap
首先观察下目前每个代的内存大小、GC方式)
jstat、jvisualvm、sar 、gclogviewer
系统运行状况的监测工具
2. 应收集到的信息
minor gc多久执行一次,full gc多久执行一次,每次耗时多少?
高峰期什么状况?
minor gc时回收的效果如何,survivor的消耗状况如何,每次有
多少对象会进入old?
old区在full gc后会消耗多少(简单的memory leak判断方法)
系统的load、cpu消耗、qps or tps、响应时间
74. GC Tuning—设定目标
调优的目标是什么呢
降低Full GC执行频率?
降低Full GC消耗时间?
降低Full GC所造成的应用暂停时间?
降低Minor GC执行频率?
降低Minor GC消耗时间?
例如某系统的GC调优目标:
降低Full GC执行频率的同时,尽可能降低minor GC的执行频率、
消耗时间以及GC对应用造成的暂停时间。
75. GC Tuning—尝试调优
根据目标针对性的寻找瓶颈以及制定调优策略
来说说常见的
降低Full GC执行频率
根据前面学习到的Full GC触发时机,寻找到瓶颈
为什么Full GC执行频率高呢,old经常满?还是old本来占用就高呢?
old为什么经常满呢?请参见PPT前面的内容...
是不是因为minor gc后经常有对象进入old呢?为什么?
注意Java RMI的定时GC触发,可通过:
-XX:+DisableExplicitGC来禁止;
或通过 -Dsun.rmi.dgc.server.gcInterval=3600000来控制触发的时间。
76. GC Tuning—尝试调优
降低Full GC执行频率 – 通常瓶颈
Old本身占用的就一直高,所以只要稍微放点对象到old,就full了;
通常原因:缓存的东西太多
oracle 10g驱动时preparedstatement cache太大
查找办法,很简单:dump then mat,bingo!
77. GC Tuning—尝试调优
降低Full GC执行频率 – 通常瓶颈
Minor GC后总是有对象不断的进入Old,导致Old不断的满
通常原因:Survivor太小了
系统响应太慢、请求量太大、每次请求分配内存多
分配的对象太大...
查找办法:dump这时就不是很好用了,需要的是能分析两次
minor GC之间到底哪些地方分配了内存;
jstat观察Survivor的消耗状况,-XX:PrintHeapAtGC
输出GC前后的详细信息;
系统响应慢那行属于系统优化,不在这里扯;
78. GC Tuning—尝试调优
降低Full GC执行频率 – 调优策略
Old本身占用的就一直高
调优办法
① 扩大old大小(减少new或调大heap);
减少new注意对minor gc的影响并且同时有可能造成full
gc还是严重;
调大heap注意full gc的时间的延长,cpu够强悍嘛,os
32 bit的吗?
② 程序优化(去掉一些不必要的缓存)
79. GC Tuning—尝试调优
降低Full GC执行频率 – 调优策略
Minor GC后总是有对象不断的进入Old
前提:这些进入Old的对象在full时大部分都会被回收
调优办法
① 降低Minor GC执行频率(等到那部分再讲)
② 让对象尽量在Minor GC中就被回收掉
放大new、放大survivor、增大TenuringThreshold
但要注意这些有可能会造成minor gc执行频繁
③ 换CMS GC
Old还没满就回收掉,从而降低Full GC触发的可能
④ 程序优化
提升响应速度、降低每次请求分配的内存
84. GC Tuning—不是瞎算的
① 系统的生辰八字
每次请求平均需要分配多少内存?
系统的平均响应时间是多少呢?
请求量是多少、多久一次Minor、Full?
② 先掐指算下
在现在的参数下,应该是多久一次
Minor、Full,对比真实状况,做一定
的偏差;
③ 根据所掌握的知识,就可以判断了
86. GC Tuning—总结
是个复杂过程,按照Tony(GC主要作者的
说法):GC Tuning is art!
综合考虑,每个参数的调整都有可能带来
其他的影响。
总结来说,提升响应速度、降低每次请求
分配的内存才是必杀技!
或者不计成本:64 bit,多个高档CPU、
大量的内存都上!
88. 内存回收
1、通常有两种实现方法:
1.1 引用计数
不适合复杂对象引用关系,尤其是有循环依赖的场景;
需要计数;
优点是只要计数器降为0,就可被回收。
1.2 跟踪(有向图Tracing)
适合于复杂对象引用关系场景,Hotspot采用这种;
需要到达某个时机后触发执行,并且通常需要暂停应用线程。
常用算法:Copying、Mark-Sweep、Mark-Compact,算法请
参见《垃圾回收》这本绝版书。
90. 内存回收
如何暂停应用线程?
safepoint first: 检测某内存页是否可读的指令
先想想:只有会改变引用关系的地方才需要暂停,否则没必要,因此对于
正在native中执行的线程,JVM是不管的,而当native代码需要改变引用
关系时,又回到了hotspot java部分的代码,而在那些代码上是会有
safepoint的。
代码编译时会在某些部分生成safepoint,当需要GC时,GC会向core vm
提交暂停应用线程的请求,core vm会将safepoint检测的内存页置为
不可读状态,当解释执行或JIT编译执行到safepoint时,发现内存页不可
读,于是就挂起了。
91. 新生代可用GC
串行GC 并行回收GC
并行GC
(Serial (Parallel
(ParNew)
Copying) Scavenge)
99. 新生代可用GC—PS
完整内存分配策略
1、先在TLAB上分配,分配失败则直接在eden上分配;
2、当eden上分配失败时,检查需要分配的大小是否 >= eden space
的一半,如是,则直接在旧生代分配;
3、如分配仍然失败,且gc已超过频率,则抛出OOM;
4、进入基本分配策略失败的模式;
5、执行PS GC,在eden上分配;
6、执行非最大压缩的full gc,在eden上分配;
7、在旧生代上分配;
8、执行最大压缩full gc,在eden上分配;
9、在旧生代上分配;
10、如还失败,回到2。
最悲惨的情况,分配触发多次PS GC和多次Full GC,直到OOM。
100. 新生代可用GC—PS
完整内存回收策略
1、如gc所执行的时间超过,直接结束;
2、先调用invoke_nopolicy
2.1 先检查是不是要尝试scavenge;
2.1.1 to space必须为空,如不为空,则返回false;
2.1.2 获取之前所有minor gc晋级到old的平均大小,并对比目前
eden+from已使用的大小,取更小的一个值,如old剩余空间
小于此值,则返回false,如大于则返回true;
2.2 如不需要尝试scavenge,则返回false,否则继续;
2.3 多线程扫描活的对象,并基于copying算法回收,回收时相应的晋升对象到旧生代;
2.4 如UseAdaptiveSizePolicy,那么重新计算to space和
tenuringThreshold的值,并调整。
3、如invoke_nopolicy返回的是false,或之前所有minor gc晋级到old的
平均大小 > 旧生代的剩余空间,那么继续下面的步骤,否则结束;
4、如UseParallelOldGC,则执行PSParallelCompact,如不是
UseParallelOldGC,则执行PSMarkSweep。
101. 旧生代可用的GC
串行GC 并行 MSC GC 并行 Compacting GC 并发GC
(Serial MSC) (Parallel MSC) (Parallel Compacting) (CMS)
111. 旧生代可用GC—并发
3. Preclean
重新扫描上一步过程中新创建的对象和引用关系改变了的对象的引
用关系;
此步什么时候执行,以及执行到什么时候再触发后续动作,取决于
两个值:-XX: CMSScheduleRemarkEdenSizeThreshold、
-XX: CMSScheduleRemarkEdenPenetration
第一个值默认为2,第二个值默认为50%,代表着当eden space
使用超过2M时,执行此步,当使用超过50%时,触发remark;
上面这个步骤有些时候有可能会引发bug,有对象需要在old分配
空间,但由于remark总是没执行,导致old空间不足,默认此步的
超时时间为5秒,可通过-XX: CMSMaxAbortablePrecleanTime
设置,单位为毫秒。
115. References
1、GC Tuning in the Hotspot
2、Our Collectors
3、CMS GC
4、why now
5、JDK 6.0 gc tuning
6、memory management in hotspot whitepaper
7、JVM内存管理和垃圾回收