• JVM参数之堆栈空间配置


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

    堆配置

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

    java -Xms20m -Xmx30m GCDemo
    
    • 1

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

    年轻代

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

    java -Xms20m -Xmn10M GCDemo
    
    • 1

    上面的命令中,我们设置 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
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

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

    Eden区

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

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

    例如我们的年轻代有 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
    
    • 1

    在上面的启动参数中,我们设置堆初始大小为 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
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    从上图可以看到: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
    
    • 1

    在上面的启动参数中,我们设置永久代初始大小为 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
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

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

    栈空间

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

    java -Xss2m GCDemo
    
    • 1

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

    直接内存

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

    java -XX:MaxDirectMemorySize=50m GCDemo
    
    • 1

    上面的启动命令设置直接内存最大值为 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

  • 相关阅读:
    52-C语言-文件问题-把字符串中的小写字母变为大写字母,并输出到磁盘文件“test”中,输入的字符串以‘!’结束
    数据仓库入门介绍
    病毒排查【备忘录】
    深度学习常见的transform和dataloder联合transform
    qemu中中断model虚拟化是如何实现的?
    mysql 的注释 -- 其后要加上空格
    HTTP状态 400 - 错误的请求:在请求目标中找到无效字符。有效字符在RFC 7230和RFC 3986中定义(Tomcat,非SpringBoot)
    如何学习和阅读代码
    你知道哪几种Java锁?分别有什么特点?
    编程随笔-Java | 02.File类常用API
  • 原文地址:https://blog.csdn.net/cljdsc/article/details/134517211