jvm内存结构
首先,我们来看看jvm堆内存结构,分别新生代、老年代,其中新生代又分为eden区和Survivore区,Survivore区又分为from区和to区;
除了堆内存之外,Java 虚拟机还有一个 非堆
的空间,这个空间由所有线程共享的方法区。方法区(也叫永久代)属于非堆内存。它存储每个类结构,如运行时常数池、字段和方法数据,以及方法和构造方法的代码。它是在 Java 虚拟机启动时创建的。
各区域的默认值
堆内存
堆空间初始值:
由-Xms指定,默认是物理内存的1/64。比如我电脑内存是16G,由此得出公式为:16*1024/64=256M
堆空间最大值:
由-Xmx指定,默认是物理内存的1/3。比如我电脑内存是16G,由此得出公式为:16/4=4G
老年代 :
占用整个堆内存2/3
的空间,比如我目前堆内存是4G,由此得出公式为:4*1024*(2/3)
$\approx$2730
新生代:
通过以下参数设置
- `-XX:NewSize=256m` : 设置新生代初始内存
- `-XX:MaxNewSize=256m` : 设置新生代最大内存
- `-Xmn256m` : 将NewSize与MaxNewSize设为一致。256m
- 新生代默认占用整个堆内存`1/3`的空间,比如我目前堆内存是4G,由此得出公式为:`4*1024*(1/3)`$\approx$`1365`,另外新生代实际可用的内存为 9/10 ( 即90% ),
新生代和老年代比例:
新老年代默认比例为:1:2,也就是说老年代会比新生代多一倍的容量,通过参数 -XX:NewRatio=3
,设置新生代与老年代的比值。设置为3,则新生代与老年代所占比值为1:3,
Survivore区:
由于Survivore分为from区和to区,且这2个区域的大小是一样的,所以eden、from、to的比例为8:1:1;如果设置参数-XX:SurvivorRatio=7
,则eden、from、to的比例为7:1.5:1.5
eden区 :
新生代减去2个Survivor区的内存大小就是Eden的大小
- 默认空余堆内存小于40%时,JVM就会增大堆直到-Xmx的最大限制;
- 空余堆内存大于70%时,JVM会减少堆直到-Xms的最小限制。
非堆内存
方法区:
通过-XX: PermSize=128m
设置持久代初始内存大小128M,-XX:MaxPermSize=512m
设置持久代最大内存大小512M,MaxPermSize
缺省值和-server -client
选项相关,-server
选项下默认MaxPermSize
为64m,-client选项下默认MaxPermSize
为32m。
栈内存
通过-Xss1m
设置,JDK5.0以后每个线程栈大小为1M,JDK1.5以前每个线程栈大小为256K,每个线程都会产生一个栈。在相同物理内存下,减小这个值能生成更多的线程。如果这个值太小会影响方法调用的深度,所以当递归调用太深的时候,就有可能耗尽栈空间,爆出StackOverflow
的错误。
在相同物理内存下,减小这个值能生成更多的线程。但是操作系统对一 个进程内的线程数还是有限制的,不能无限生成,经验值在3000~5000左右。
线程栈的大小是个双刃剑
· 如果设置过小,可能会出现栈溢出,特别是在该线程内有递归、大的循环时出现溢出的可能性更大,
· 如果该值设置过大,就有影响到创建栈的数量,如果是多线程的应用,就会出现内存溢出的错误.
堆空间各区域大小划分
在调优之前,我们首先要知道目前项目中长期存活的对象占用了多大的内存空间,也就是老年代的空间有多少活跃数据,这个数据可以通过查看GC日志得到,就是每次fullgc后老年代还有多少空间,因为每次的值都不一样,所以我们可以取一个平均值;得到这个平均值后,就可以设置堆空间区域大小了,下图是一个参考标准,接下来我们就以这参考图来设置各空间的大小
比如我项目中老年代活跃数据的大小为500M,在这里我们都取一个最大值,那么我们就可以这样设置
- 总大小:设置堆空间总大小公式为:
500*4=2000M
,将堆内存最大和初始内存都设为2000,jvm参数为:-Xmx2000m -Xms2000m
- 新生代:设置堆空间的新生代大小公式为:
500*1=500M
,jvm参数为:-XX:NewSize=500m -XX:MaxNewSize=500m
- 老年代:设置堆空间的老年代大小公式为:
500*3=1500
:因为老年代只能设置比例,所以我们设置新生代和老年代的比例为1:3
,jvm参数为:-XX:NewRatio=3
,这样经过换算下来,老年代就有1500M
的空间了 - 永久代/元空间 :注意这里取值必须是full gc 后的永久代空间,比如经过GC清理后,我的永久代活跃数据为100m,那我们就设置为150M;jvm参数为:
-XX: PermSize=150m --XX: PermSize=150m
完
以上调优是为大家做一个标准模板,实际上调优的话还得通过具体环境和条件在计算出配置的大小;当然,在大部分项目情况下也已经够用了;