查看 JVM 内存的占用情况

编写代码

package com.test;

public class PrintMemoryDemo {
    public static void main(String[] args) {
        // 堆内存总量
        long totalMemory = Runtime.getRuntime().totalMemory();
        // jvm 试图使用的最大堆内存
        long maxMemory = Runtime.getRuntime().maxMemory();
        // jvm 剩余的堆内存
        long freeMemory = Runtime.getRuntime().freeMemory();

        System.out.println("jvm 堆内存总量: " + totalMemory/1024/1024 + "MB");
        System.out.println("jvm 最大内存: " + maxMemory/1024/1024 + "MB");
        System.out.println("jvm 剩余内存: " + freeMemory/1024/1024 + "MB");
    }
}

运行程序

$ java com.test.PrintMemoryDemo
jvm 堆内存总量: 366MB
jvm 最大内存: 5431MB
jvm 剩余内存: 362MB

本机的内存为 24G,初始化堆内内存为 366M,接近本机的 1/64(24G/64=384M)。最大堆内存为 5431M,接近本机的 1/4(24G/4=6144M)

Eden 区优先分配

首先我们需要了解下 JVM 的四种垃圾收集场景:

  • Minor GC 和 Young GC 都是指新生代的垃圾收集。
  • Major GC 和 Old GC 都是指老年代的垃圾收集。(CMS 收集器专属)
  • Mixed GC 混合收集,指收集整个新生代以及部分老年代的垃圾收集。(G1 收集器专属)
  • Full GC 整堆收集,指收集整个 Java 堆和方法区垃圾收集。

新生代和老年代在 Java 虚拟机内存体系 着重介绍了,我们在这里还是简单复习下。 堆分为新生代 + 老年代,新生代又被划分为 Eden 区和 Survivor 区,Survivor 又被划分为两个相等大小的 s0 和 s1 区。其中 Eden 区大小和 Survivor 区大小是 8:1。默认情况下新生代占堆的 1/3,老年代占堆的 2/3。 如下图所示,气泡中的数字代表占用内存的份数,比如 Eden 区占用:

 大多数情况下,新创建的对象都会在新生代的 Eden 区中分配,当 Eden 区没有足够的空间分配时,虚拟机将会发生一次 Minor GC。 下面我们设置程序的堆大小为 30 MB,且不可扩大。

编写代码

package com.test;

public class EdenAllocationDemo {
    // 定义占用 1MB 空间的变量
    private static final int _1MB = 1024 * 1024;

    public static void main(String[] args) {
        byte[] allocation1, allocation2, allocation3, allocation4;
        allocation1 = new byte[2 * _1MB];
        allocation2 = new byte[2 * _1MB];
        allocation3 = new byte[2 * _1MB];
        allocation4 = new byte[4 * _1MB];

    }
}

执行程序

$ java -Xms30m -Xmx30m -Xmn10m -XX:SurvivorRatio=8 -XX:+PrintGCDetails com.test.EdenAllocationDemo
Heap
 PSYoungGen      total 9216K, used 7456K [0x00000000ff600000, 0x0000000100000000, 0x0000000100000000)
  eden space 8192K, 91% used [0x00000000ff600000,0x00000000ffd48008,0x00000000ffe00000)
  from space 1024K, 0% used [0x00000000fff00000,0x00000000fff00000,0x0000000100000000)
  to   space 1024K, 0% used [0x00000000ffe00000,0x00000000ffe00000,0x00000000fff00000)
 ParOldGen       total 20480K, used 4096K [0x00000000fe200000, 0x00000000ff600000, 0x00000000ff600000)
  object space 20480K, 20% used [0x00000000fe200000,0x00000000fe600010,0x00000000ff600000)
 Metaspace       used 2678K, capacity 4486K, committed 4864K, reserved 1056768K
  class space    used 289K, capacity 386K, committed 512K, reserved 1048576K

命令行解释:

  • -Xms30M 最小堆内存 30 M。
  • -Xmx30M 最大堆内存 30 M。
  • -Xmn10M 新生代占用堆内存 10 M。
  • -XX:SurvivorRatio=8 新生代中的 Eden 区和新生代中的一个 Survivor 区比例为 8:1。
  • 堆中的新生代占用 10 MB,占用堆内存的 1/3,剩下的 20 MB 分配给老年代。新生代中的一个 Survivor 区占用 1/10(1 MB),另外一个 Survivor 区占用 1/10(1 MB),Eden 区占用 8/10(8 MB)。

结果分析:

新生代总内存 9216K (Eden区 + 1个Suvivor区,已使用 7456K)
    新生代Eden区 8192K(8M),使用 7456/8162=91%
        from suvivor 区 1024 K,已使用 0%
        to suvivor 区 1024 K,已使用 0%
老年代总内存 20480 K,已使用 4096 K,4096/20480=20%

创建的 allocation1、allocation2、allocation3 分配到了 Eden 区,占用 6M,当分配 allocation4(需要4M) 时,因为新生代内总内存总共只有 9M(8M Eden 区 + 1M Suvivor 区) ,allocation4 不能放进 Eden 区,直接放到了老年代。

如果您觉得文章对您有帮助,欢迎留言打赏,感谢您的支持! 

Logo

华为开发者空间,是为全球开发者打造的专属开发空间,汇聚了华为优质开发资源及工具,致力于让每一位开发者拥有一台云主机,基于华为根生态开发、创新。

更多推荐