JVM内存分配策略

| 分类 java之内存管理  | 标签 java  JVM  HotSpot  GC  内存    垃圾回收  Full-GC  新生代  老年代  Serial  ParNew  Parallel  Eden  Survivor  Tenured 

本文以这个简单的代码为例分析特定JVM 配置下的内存分配策略

public class TestGC {
    
    public static void main(String[] args)  
    {
        byte[] b1 = new byte[2 * 1024 * 1024];   // 2M
        byte[] b2 = new byte[2 * 1024 * 1024];   // 2M
        byte[] b3 = new byte[2 * 1024 * 1024];   // 2M
        

        byte[] b4 = new byte[4 * 1024 * 1024];   // 4M
        
        System.gc();
    }
}

介绍一下相关JVM参数

本次例子,设置JVM 的参数为

-verbose:gc
-XX:+PrintGCDetails
-Xms20M
-Xmx20M
-Xmn10M
-XX:SurvivorRatio=8

前两个参数-verbose:gc、-XX:+PrintGCDetails 用于让JVM 输出GC 信息

-Xms20M、-Xmx20M 设置堆初始容量大小,以及堆最大容量大小

-Xmn 这个参数则是对-XX:NewSize、-XX:MaxNewSize 两个参数的同时配置,也就是说可以通过-Xmn 来配置新生代的内存大小,相当于上面说的那两个参数设置一样的值

-XX:SurvivorRatio=8,两个Survivor 区与一个Eden 区的比值为2:8,一个Survivor 区占整个新生代的1/10

所以目前JVM 的内存情况是这样的:堆内存总共20M,年轻代有10M,年轻代中Eden 大小为8M,每个Survivor 的大小是1M

学会看GC日志

在Hotspot JDK1.8 环境下运行的输入如下

[GC (Allocation Failure) [DefNew: 7003K->667K(9216K), 0.0161421 secs] 7003K->6811K(19456K), 0.0162755 secs] [Times: user=0.02 sys=0.00, real=0.02 secs] 
[Full GC (System.gc()) [Tenured: 6144K->6144K(10240K), 0.0032884 secs] 11069K->10906K(19456K), [Metaspace: 1763K->1763K(4480K)], 0.0033704 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 
Heap
 def new generation   total 9216K, used 4926K [0x04600000, 0x05000000, 0x05000000)
  eden space 8192K,  60% used [0x04600000, 0x04acfa40, 0x04e00000)
  from space 1024K,   0% used [0x04f00000, 0x04f00000, 0x05000000)
  to   space 1024K,   0% used [0x04e00000, 0x04e00000, 0x04f00000)
 tenured generation   total 10240K, used 6144K [0x05000000, 0x05a00000, 0x05a00000)
   the space 10240K,  60% used [0x05000000, 0x05600030, 0x05600200, 0x05a00000)
 Metaspace       used 1767K, capacity 2242K, committed 2368K, reserved 4480K

重点介绍一下怎么看GC 日志!

首先来看一下这行日志的含义

[GC (Allocation Failure) [DefNew: 7003K->667K(9216K), 0.0161421 secs] 7003K->6811K(19456K), 0.0162755 secs] [Times: user=0.02 sys=0.00, real=0.02 secs] 

GC 日志的开头[GC 和[Full GC 说明了这次垃圾收集的停顿类型,而不是用来区分新生代GC 还是老年代GC 的,如果有Full,说明这次GC 发生了Stop-The-World(STW)。比如调用System.gc() 会导致一次STW

接下来的[DefNew、[Tenured 表示GC 发生的区域,不同的垃圾收集器命名会不一致,以新生代为例

  • [DefNew 是指Default New Generation,是Serial 垃圾收集器中的新生代名称
  • [ParNew 是指ParNew 垃圾收集器中的新生代名称
  • [PSYoungGen 是指在Parallel Scavenge 垃圾收集器中的新生代名称

再看后面的方括号内的7003K->667K(9216K),表示“GC 前该内存区域已使用容量 -> GC 后该内存区域已使用容量 (该内存区域总容量)”;方括号外的7003K->6811K(19456K),表示“GC 前堆已使用容量 -> GC 后堆已使用容量 (堆总容量)”

再往后0.0162755 secs 表示该内存区域GC 所用的时间,单位是秒,这个信息后面的Times 则是更具体的时间数据

接下来看一下这部分日志是什么含义

Heap
 def new generation   total 9216K, used 4926K [0x04600000, 0x05000000, 0x05000000)
  eden space 8192K,  60% used [0x04600000, 0x04acfa40, 0x04e00000)
  from space 1024K,   0% used [0x04f00000, 0x04f00000, 0x05000000)
  to   space 1024K,   0% used [0x04e00000, 0x04e00000, 0x04f00000)
 tenured generation   total 10240K, used 6144K [0x05000000, 0x05a00000, 0x05a00000)
   the space 10240K,  60% used [0x05000000, 0x05600030, 0x05600200, 0x05a00000)
 Metaspace       used 1767K, capacity 2242K, committed 2368K, reserved 4480K

这部分日志是系统运行完前的快照

eden space 8192K,正如我们上面再设置JVM 参数时分析的那样,Eden 区域是8M 大小。这里有60% 的被使用了?为什么?这点我们最后分析

from space 1024K,这是其中一个Survivor,也和我们之前分析的一样,是1M 大小;to space 1024K 同理

tenured generation total 10240K,根据上面的参数配置,整个堆的大小是20M,一个Eden 8M,两个Survivor 分别是1M,剩下刚好10M 是Tenured 的大小

分析程序运行细节

再重复一下JVM 的配置

  • 整个堆的大小是20M
  • Eden 的大小是8M
  • 两个Survivor 分别是1M
  • Tenured 的大小是10M

逐行分析上面的代码!

首先申请一个2M 的堆内存,因为Eden 的空间是够用的,所以新创建的对象就优先在Eden 分配内存

再次申请一个2M 的堆内存,Eden 的空间还是够用的,所以新创建的对象就优先在Eden 分配内存

再次申请一个2M 的堆内存,Eden 的空间还是够用的,所以新创建的对象就优先在Eden 分配内存

现在Eden 还剩下 8-2-2-2 = 2M

最后要申请一个4M 的堆内存,Eden 只剩下2M,明显不够用了,这次就触发了一次GC,而Survivor 的大小只有1M,所以只能把把Eden 中3个 2M 的对象移到Tenured 分区中去,对应的就是第一个GC 日志

[GC (Allocation Failure) [DefNew: 7003K->667K(9216K), 0.0161421 secs] 7003K->6811K(19456K), 0.0162755 secs] [Times: user=0.02 sys=0.00, real=0.02 secs] 

然后Eden 就又有空间了,这个4M 的对象就可以放到这里了

所以最后Eden 中有60% used(总共8M,这个对象用了4M,估计还有一些有其他用);Tenured 有60% used(总共10M,用了6M)




如果本篇文章对您有所帮助,您可以通过微信(左)或支付宝(右)对作者进行打赏!


上一篇     下一篇