JVM参数之堆栈空间配置

JVM 中最重要的一部分就是堆空间了,基本上大多数的线上 JVM 问题都是因为堆空间造成的 OutOfMemoryError。因此掌握 JVM 关于堆空间的参数配置对于排查线上问题非常重要。

堆配置

我们使用 -Xms 设置堆的初始空间大小,使用 -Xmx 设置堆的最大空间大小。

java -Xms20m -Xmx30m GCDemo

在上面的命令中,我们设置 JVM 的初始堆大小为 20M,最大堆空间为 30M。

年轻代

在 JDK1.8 中,堆分为年轻代和老年代。JVM 提供了参数 -Xmn 来设置年轻代内存的大小,但没有提供参数设置老年代的大小。但其实老年代的大小就等于堆大小减去年轻代大小。

java -Xms20m -Xmn10M GCDemo

上面的命令中,我们设置 JVM 堆初始大小为20M。其中年轻代的大小为 10M,那么剩下的就是老年代的大小,有 10M了。 我们可以给上述命令加上 -XX:+PrintGCDetails 参数来查看内存区域的分配信息。

# java -Xms20m -Xmn10M -XX:+PrintGCDetails 
Heap
 PSYoungGen      total 9216K, used 671K [0x00000000ff600000, 0x0000000100000000, 0x0000000100000000)
  eden space 8192K, 8% used [0x00000000ff600000,0x00000000ff6a7dc8,0x00000000ffe00000)
  from space 1024K, 0% used [0x00000000fff00000,0x00000000fff00000,0x0000000100000000)
  to   space 1024K, 0% used [0x00000000ffe00000,0x00000000ffe00000,0x00000000fff00000)
 ParOldGen       total 10240K, used 0K [0x0000000085c00000, 0x0000000086600000, 0x00000000ff600000)
  object space 10240K, 0% used [0x0000000085c00000,0x0000000085c00000,0x0000000086600000)
 Metaspace       used 2540K, capacity 4480K, committed 4480K, reserved 1056768K
  class space    used 283K, capacity 384K, committed 384K, reserved 1048576K

如上图所示,我们可以看到老年代的大小为 10M。

Eden区

在年轻代中,分为三个区域,分别是:eden 空间、from 空间、to 空间。如果要设置这部分的大小,那么就使用 -XX:SurvivorRatio 这个参数,该参数设置 eden / from 空间的比例关系,该参数的公式如下:

-XX:SurvivorRatio = eden/from = eden/to

例如我们的年轻代有 10 M,而我们设置 -XX:SurvivorRatio 参数为 2。也就是说 eden / from = eden / to = 2。这里教一个快速计算的方法,我们假设 eden = 2,那么 from = 1,to = 1,那么 eden + from + to = 10M。这样就可以算出每一份大小是 10/4 = 2.5M。所以 Eden 区 = 2.5 * 2 = 5M,from 区是 2.5 M,to 区是 2.5 M。

下面我们运行下命令来验证一下。

java -Xms20m -Xmn10M -XX:SurvivorRatio=2 -XX:+PrintGCDetails GCDemo

在上面的启动参数中,我们设置堆初始大小为 20M,年轻代大小为 10M,年轻代的 SurvivorRatio 比例为 2。那么最终分配的结果将会是:年轻代 10M,其中 Eden 区 5M、From 区 2.5M、To 区 2.5 M,老年代 10M。

# java -Xms20m -Xmn10M -XX:SurvivorRatio=2 -XX:+PrintGCDetails GCDemo
Error: Could not find or load main class GCDemo
Heap
 PSYoungGen      total 7680K, used 631K [0x00000000ff600000, 0x0000000100000000, 0x0000000100000000)
  eden space 5120K, 12% used [0x00000000ff600000,0x00000000ff69dc20,0x00000000ffb00000)
  from space 2560K, 0% used [0x00000000ffd80000,0x00000000ffd80000,0x0000000100000000)
  to   space 2560K, 0% used [0x00000000ffb00000,0x00000000ffb00000,0x00000000ffd80000)
 ParOldGen       total 10240K, used 0K [0x0000000085c00000, 0x0000000086600000, 0x00000000ff600000)
  object space 10240K, 0% used [0x0000000085c00000,0x0000000085c00000,0x0000000086600000)
 Metaspace       used 2540K, capacity 4480K, committed 4480K, reserved 1056768K
  class space    used 283K, capacity 384K, committed 384K, reserved 1048576K

从上图可以看到:eden 空间是 5120 K,from 和 to 空间是 2560 K。

上图还有一个细节,即 PSYoungGen 这里的 total 只有 7680K,难道年轻代只有 7.5M 的内存吗?为什么不是 10M 呢?其实是因为这里的 total 指的是可用内存,from space 和 to space 两个区域,同一时间只有一个区域是可以用的。所以可用内存是 5120 + 2560 = 7680。

永久代(JDK1.7)

在 JDK 1.8 之前,所加载的类信息都放在永久代中。我们用 -XX:PermSize 设置永久代初始大小,用 -XX:MaxPermSize 设置永久代最大大小。

java -XX:PermSize10m -XX:MaxPermSize50m -XX:+PrintGCDetails GCDemo

在上面的启动参数中,我们设置永久代初始大小为 10M,最大大小为 50M。我们在 JDK1.7 的环境下运行上面的命令,会看到如下的 GC 日志。

java -XX:PermSize10m -XX:MaxPermSize50m -XX:+PrintGCDetails GCDemo
Java HotSpot(TM) 64-Bit Server VM warning: ignoring option PermSize10m; support was removed in 8.0
Java HotSpot(TM) 64-Bit Server VM warning: ignoring option MaxPermSize50m; support was removed in 8.0
Error: Could not find or load main class GCDemo
Heap
 PSYoungGen      total 36864K, used 1269K [0x00000000d7400000, 0x00000000d9d00000, 0x0000000100000000)
  eden space 31744K, 4% used [0x00000000d7400000,0x00000000d753d728,0x00000000d9300000)
  from space 5120K, 0% used [0x00000000d9800000,0x00000000d9800000,0x00000000d9d00000)
  to   space 5120K, 0% used [0x00000000d9300000,0x00000000d9300000,0x00000000d9800000)
 ParOldGen       total 84992K, used 0K [0x0000000085c00000, 0x000000008af00000, 0x00000000d7400000)
  object space 84992K, 0% used [0x0000000085c00000,0x0000000085c00000,0x000000008af00000)
 Metaspace       used 2541K, capacity 4480K, committed 4480K, reserved 1056768K
  class space    used 283K, capacity 384K, committed 384K, reserved 1048576K

从上面的执行结果可以看到,Metaspace 空间的大小为 2.6M 左右,并不是我们设置的 10M。那是因为 MetaspaceSize 设置的是元空间发生 GC 的初始阈值。当达到这个值时,元空间发生 GC 操作,这个值默认是 20.8M。而 MaxMetaspaceSize 则是设置元空间的最大大小,默认基本是机器的物理内存大小。虽然可以不设置,但还是建议设置一下,因为如果一直不断膨胀,那么 JVM 进程可能会被 OS kill 掉。

栈空间

栈空间是每个线程各自有的一块区域,如果栈空间太小,也会导致 StackOverFlow 异常。而要设置栈空间大小,只需要使用 -Xss 参数就可以。

java -Xss2m GCDemo

上面的启动命令设置最大栈空间为 2M。

直接内存

在 JVM 中还有一块内存,它独立于 JVM 的堆内存,它就是:直接内存。我们可以使用 -XX:MaxDirectMemorySize 设置最大直接内存。如果不设置,默认为最大堆空间,即 -Xmx。

java -XX:MaxDirectMemorySize=50m GCDemo

上面的启动命令设置直接内存最大值为 50M。

当直接内存使用达到设置值时,就会触发垃圾回收。如果不能有效释放足够空间,就会引发直接内存溢出导致系统的 OOM。

总结

参数含义
-Xms初始堆大小
-Xmx最大堆空间
-Xmn设置新生代大小
-XX:SurvivorRatio设置新生代eden空间和from/to空间的比例关系
-XX:PermSize方法区初始大小
-XX:MaxPermSize方法区最大大小
-XX:MetaspaceSize元空间GC阈值(JDK1.8)
-XX:MaxMetaspaceSize最大元空间大小(JDK1.8)
-Xss栈大小
-XX:MaxDirectMemorySize直接内存大小,默认为最大堆空间)
-XX:+PrintGC输出GC日志
-XX:+PrintGCDateStamps输出GC的时间戳(以日期的形式)
-XX:+PrintGCTimeStamps输出GC的时间戳(以基准时间的形式)
-XX:+PrintGCDetails输出GC的详细日志
-Xloggcgc日志文件的输出路径
-XX:+UseConcMarkSweepGC设置年老代为并发收集
-XX:+UseParNewGC设置年轻代为并发收集
-XX:ParallelGCThreads配置并行收集器的线程数
-XX:+UseCMSCompactAtFullCollection打开对年老代的压缩
-XX:CMSFullGCsBeforeCompaction设置运行次FullGC以后对内存空间进行压缩、整理
-XX:CMSInitiatingOccupancyFraction设置年老代空间到?%时就开始执行CMS

link

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

杰哥的技术杂货铺

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值