JVM 中最重要的一部分就是堆空间了,基本上大多数的线上 JVM 问题都是由于堆空间形成的 OutOfMemoryError。所以掌握 JVM 关于堆空间的参数配置对于排查线上问题很是重要。html
tips:本文全部配置,如无特别说明,均基于JDK1.8。java
咱们使用 -Xms 设置堆的初始空间大小,使用 -Xmx 设置堆的最大空间大小。jvm
java -Xms20m -Xmx30m GCDemo
在上面的命令中,咱们设置 JVM 的初始堆大小为 20M,最大堆空间为 30M。性能
在 JDK1.8 中,堆分为年轻代和老年代。JVM 提供了参数 -Xmn 来设置年轻代内存的大小,但没有提供参数设置老年代的大小。但其实老年代的大小就等于堆大小减去年轻代大小。学习
java -Xms20m -Xmn10M GCDemo
上面的命令中,咱们设置 JVM 堆初始大小为20M。其中年轻代的大小为 10M,那么剩下的就是老年代的大小,有 10M了。 咱们能够给上述命令加上-XX:+PrintGCDetails
参数来查看内存区域的分配信息。spa
如上图所示,咱们能够看到老年代的大小为 10M。线程
在年轻代中,分为三个区域,分别是:eden 空间、from 空间、to 空间。若是要设置这部分的大小,那么就使用 -XX:SurvivorRatio 这个参数,该参数设置 eden / from 空间的比例关系,该参数的公式以下:日志
-XX:SurvivorRatio = eden/from = eden/to
code
例如咱们的年轻代有 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。htm
下面咱们运行下命令来验证一下。
java -Xms20m -Xmn10M -XX:SurvivorRatio=2 -XX:+PrintGCDetails GCDemo
在上面的启动参数中,咱们设置堆初始大小为 20M,年轻代大小为 10M,年轻代的 SurvivorRatio 比例为 2。那么最终分配的结果将会是:年轻代 10M,其中 Eden 区 5M、From 区 2.5M、To 区 2.5 M,老年代 10M。
从上图能够看到:eden 空间是 5120 K,from 和 to 空间是 2560 K。
上图还有一个细节,即 PSYoungGen 这里的 total 只有 7680K,难道年轻代只有 7.5M 的内存吗?为何不是 10M 呢?实际上是由于这里的 total 指的是可用内存,from space 和 to space 两个区域,同一时间只有一个区域是能够用的。因此可用内存是 5120 + 2560 = 7680。
在 JDK 1.8 以前,所加载的类信息都放在永久代中。咱们用 -XX:PermSize 设置永久代初始大小,用 -XX:MaxPermSize 设置永久代最大大小。
java -XX:PermSize10m -XX:MaxPermSize50m -XX:+PrintGCDetails GCDemo
在上面的启动参数中,咱们设置永久代初始大小为 10M,最大大小为 50M。咱们在 JDK1.7 的环境下运行上面的命令,会看到以下的 GC 日志。
在上图中,咱们能够看到永久代的大小为咱们设置的 10M。
在 JDK 1.8 以前,全部加载的类信息都放在永久代中。但在 JDK1.8 之时,永久代被移除,取而代之的是元空间(Metaspace)。在元空间这块内存中,有两个参数很类似,它们是: -XX:MetaspaceSize 和 -XX:MaxMetaspaceSize。
java -XX:MetaspaceSize=10m -XX:MaxMetaspaceSize=50m -XX:+PrintGCDetails GCDemo
上面的命令中,咱们设置 MetaspaceSize 为 10M,MaxMetaspaceSize 为 50M。但其实它们并非设置初始大小和最大大小的。
从上面的执行结果能够看到,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 | 直接内存大小,默认为最大堆空间 |
若是只是看,其实没法真正学会知识的。为了帮助你们更好地学习,我建了一个虚拟机群,专门讨论学习 Java 虚拟机方面的内容,每周针对我所发文章进行讨论答疑。若是你有兴趣,关注「Java技术精选」公众号,经过右下角菜单「入群交流」加我好友,小助手会拉你入群。