JVM参数调优-设置堆、新生代、老年代、持久代大小
一、根据程序的运行状况查看其活跃的数据量
①、活跃的数据:
1.应用程序运行于稳定状态时,老年代占用的java堆大小
2.应用程序运行于稳定状态时,永久代占用的java堆大小
其实就是FullGc后这2个数据的大小
②、动手测试:
1.测试代码:
-
public class A {
-
int[] storage = new int[102400];
-
int[] extra = new int[200000];
-
-
public int[] execute() {
-
try {
-
Thread.sleep(10);
-
} catch (InterruptedException e) {
-
e.printStackTrace();
-
}
-
return storage;
-
}
-
}
-
import java.util.LinkedList;
-
import java.util.Queue;
-
-
public class Test {
-
-
public static void main(String[] args) {
-
Queue<int[]> queue = new LinkedList<int[]>();
-
while (true) {
-
int[] tmp=new A().execute();
-
if (queue.size() > 1000) {
-
queue.poll();
-
} else {
-
queue.offer(tmp);
-
}
-
}
-
-
}
-
}
2.参数配置:
3.测试的结果
4.结果分析
可以看到老年代的大小为404211K,持久代的大小为8888K
二、通过活跃的数据配置堆以及其他参数
通用法则1:
将java堆的初始值-Xms和最大值-Xmx设置为老年代活跃数据大小的3~4倍
所以此处就设置 -Xms1600m,-Xmx1600m
在以上的测试结果中可以看到原始的堆大小为1675m左右,是比较接近的。
通用法则2:
永久带的初始值-XX:PermSize及最大值-XX:MaxPermSize应该比永久代活跃数据大1.2~1.5倍
所以此处就设置-XX:PermSize=13m,-XX:MaxPermSize=13m
补充法则:
新生代空间应该为老年代空间活跃数据的1~1.5倍
此处老年代为400m,新生代就为600m
如果java堆的初始值及最大值为活跃数据的3~4倍,新生代为活跃数据的1~1.5倍时,老年代应设置为活跃数据大小的2~3倍
参考表
四、日志分析
分析:
1.看频率,可以发现基本上是4秒进行一次Gc
2.看耗时,可以发现基本上是需要0.037秒
当前的虚拟机设置为:
堆:1600M
年轻代:600M
年老代:1000M
优化
需求1:
要求MinorGc的响应时间小于0.02秒
分析:
需要减少MinorGc的时间,那一定是因为年轻代的容量太大了,以至于一次清理耗时较久。
所以需要较少年轻代的大小,虽然这样会增加MinorGc的频率。
因为目前耗时是0.037,几乎是2倍于目标,所以将年轻代的大小减少为1/2,保持年老带不变。
-Xms1300m -Xmx1300m -Xmn300m
需求2:
要求MinorGc的响应时间小于0.1秒
既然对响应要求不是那么严格,就可以增大年轻代的空间,减少minorGc的频率。
现在是0.037,目标是0.1。假定是正比,那么可以把年轻代的大小提2.5倍(+900m)。
-Xms2500,m -Xmx2500m -Xmn1500m
准则
1.老年代空间大小不应该小于活跃数据大小的1.5倍
2.新生代空间至少应为Java堆的10%,,通过-Xmx和-Xms可以设定该值。
3.增大Java堆大小时,需要注意不要超过JVM可用的物理内存数。
这个阶段中,如果只考虑minorGc引起的延迟,而调整新生代的大小又无法满足应用程序的平均停顿时间或延迟性要求,就只能修改应用程序或者改变JVM的部署模式,在多个JVM上部署应用程序,或者修改应用程序的平均延迟性要求。
五.计算老年代
日志图:
1.假如没有FullGc,如何计算老年代需要多少时间装满?
(不看第一条和最后一条数据,分析)通过MinorGc计算每次老年代的增量
老年代的增量 = 堆的总量 - 新生代的剩余量(souvivor区)
这样可以得到几乎每次minorGc老年代增加85m空间,而minorGc每4秒一次。
老年代的总量:
1654272 - 601088 = 1053184k =1028m
那么填满老年区需要
1028 / 85 * 4 = 48s