这个演示中的 JVM 内存分配和释放解释?

JVM memory allocation and deallocation in this demo explained?

我有一个简单的演示来检查 JVM 内存分配和释放的细节。

Java版本

$ java -version
java version "1.8.0_201"
Java(TM) SE Runtime Environment (build 1.8.0_201-b09)
Java HotSpot(TM) 64-Bit Server VM (build 25.201-b09, mixed mode)

演示

/**
 * VM Options: -Xms20M -Xmx20M -Xmn10M -XX:+PrintGCDetails -XX:SurvivorRatio=8
 */
public class DefaultCollector {
    private static final int _1MB = 1024 * 1024;
    public static void main(String... args) {
        byte[] arr1 = new byte[2 * _1MB];
        byte[] arr2 = new byte[2 * _1MB];
        byte[] arr3 = new byte[2 * _1MB];
        byte[] arr4 = new byte[4 * _1MB];
    }
}

GC 日志

[GC (Allocation Failure) [PSYoungGen: 6516K->695K(9216K)] 6516K->4791K(19456K), 0.0019189 secs] [Times: user=0.00 sys=0.00, real=0.01 secs] 
Heap
 PSYoungGen      total 9216K, used 7408K [0x00000000ff600000, 0x0000000100000000, 0x0000000100000000)
  eden space 8192K, 81% used [0x00000000ff600000,0x00000000ffc8e6a8,0x00000000ffe00000)
  from space 1024K, 67% used [0x00000000ffe00000,0x00000000ffeadcb0,0x00000000fff00000)
  to   space 1024K, 0% used [0x00000000fff00000,0x00000000fff00000,0x0000000100000000)
 ParOldGen       total 10240K, used 4096K [0x00000000fec00000, 0x00000000ff600000, 0x00000000ff600000)
  object space 10240K, 40% used [0x00000000fec00000,0x00000000ff000020,0x00000000ff600000)
 Metaspace       used 3273K, capacity 4556K, committed 4864K, reserved 1056768K
  class space    used 357K, capacity 392K, committed 512K, reserved 1048576K
Disconnected from the target VM, address: '127.0.0.1:38815', transport: 'socket'

我的问题

  1. -Xms20M -Xmx20M -Xmn10M -XX:SurvivorRatio=8已经设置好了,eden - 8M, from - 1M, and to - 1M也显示的很清楚,为什么PSYoungGen total 9216K而不是10240K
  2. 为什么 Allocation Failure 因为 Tenured 中还有空格,并且 -XX:+HandlePromotionFailure 默认应该是 true 因为 JDK 6已经开始了吗?
  3. 为什么PSYoungGen: 6516K->695K(9216K)三个数组在新生代还活着?是因为分配失败吗?
  4. 为什么 Metaspace 占用这么多内存 Metaspace used 3273K?如果不只是键入信息,那里面到底有什么?

我也试过其他组合:

  1. 大小:1, 1, 1, 4 => eden 70%, tenured 40%

    为什么?不应该都在伊甸园吗?

  2. 大小:1, 1, 1, 1 => eden 80% (~6M), tenured 0%

    为什么在 eden 中是 80%(~6M)而不是 4M?

已更新

在@Stephen C 的帮助下,我发现在登录 IntelliJ Terminal 之前有一个输出 as

/usr/lib/jvm/java-8-oracle/bin/java -agentlib:jdwp=transport=dt_socket,address=127.0.0.1:42103,suspend=y,server=n -Xms20M -Xmx20M -Xmn10M -XX:+PrintGCDetails -XX:SurvivorRatio=8 -javaagent:/home/hearen/Downloads/idea-IU-183.5429.30/lib/rt/debugger-agent.jar -Dfile.encoding=UTF-8 -classpath /usr/lib/jvm/java-8-oracle/jre/lib/charsets.jar:/usr/lib/jvm/java-8-oracle/jre/lib/deploy.jar:/usr/lib/jvm/java-8-oracle/jre/lib/ext/cldrdata.jar:/usr/lib/jvm/java-8-oracle/jre/lib/ext/dnsns.jar:/usr/lib/jvm/java-8-oracle/jre/lib/ext/jaccess.jar:/usr/lib/jvm/java-8-oracle/jre/lib/ext/jfxrt.jar:/usr/lib/jvm/java-8-oracle/jre/lib/ext/localedata.jar:/usr/lib/jvm/java-8-oracle/jre/lib/ext/nashorn.jar:/usr/lib/jvm/java-8-oracle/jre/lib/ext/sunec.jar:/usr/lib/jvm/java-8-oracle/jre/lib/ext/sunjce_provider.jar:/usr/lib/jvm/java-8-oracle/jre/lib/ext/sunpkcs11.jar:/usr/lib/jvm/java-8-oracle/jre/lib/ext/zipfs.jar:/usr/lib/jvm/java-8-oracle/jre/lib/javaws.jar:/usr/lib/jvm/java-8-oracle/jre/lib/jce.jar:/usr/lib/jvm/java-8-oracle/jre/lib/jfr.jar:/usr/lib/jvm/java-8-oracle/jre/lib/jfxswt.jar:/usr/lib/jvm/java-8-oracle/jre/lib/jsse.jar:/usr/lib/jvm/java-8-oracle/jre/lib/management-agent.jar:/usr/lib/jvm/java-8-oracle/jre/lib/plugin.jar:/usr/lib/jvm/java-8-oracle/jre/lib/resources.jar:/usr/lib/jvm/java-8-oracle/jre/lib/rt.jar:/home/hearen/git/personal/about-java/target/classes:/usr/lib/jvm/java-8-oracle/lib/dt.jar:/usr/lib/jvm/java-8-oracle/lib/tools.jar:/usr/lib/jvm/java-8-oracle/lib/sa-jdi.jar:/usr/lib/jvm/java-8-oracle/lib/jconsole.jar:/usr/lib/jvm/java-8-oracle/lib/packager.jar:/usr/lib/jvm/java-8-oracle/lib/javafx-mx.jar:/usr/lib/jvm/java-8-oracle/lib/ant-javafx.jar:/home/hearen/.m2/repository/org/projectlombok/lombok/1.16.20/lombok-1.16.20.jar:/home/hearen/.m2/repository/junit/junit/4.11/junit-4.11.jar:/home/hearen/.m2/repository/org/hamcrest/hamcrest-core/1.3/hamcrest-core-1.3.jar:/home/hearen/.m2/repository/cglib/cglib/3.2.4/cglib-3.2.4.jar:/home/hearen/.m2/repository/org/ow2/asm/asm/5.1/asm-5.1.jar:/home/hearen/.m2/repository/org/apache/ant/ant/1.9.6/ant-1.9.6.jar:/home/hearen/.m2/repository/org/apache/ant/ant-launcher/1.9.6/ant-launcher-1.9.6.jar:/home/hearen/Downloads/idea-IU-183.5429.30/lib/idea_rt.jar jvm.allocation.DefaultCollector

然后我手动使用 CLI 编译和运行程序

$ javac DefaultCollector.java 
$ java -Xms20M -Xmx20M -Xmn10M -XX:+PrintGCDetails -XX:SurvivorRatio=8 DefaultCollector

然后输出变成

Heap
 PSYoungGen      total 9216K, used 6815K [0x00000000ff600000, 0x0000000100000000, 0x0000000100000000)
  eden space 8192K, 83% used [0x00000000ff600000,0x00000000ffca7ff8,0x00000000ffe00000)
  from space 1024K, 0% used [0x00000000fff00000,0x00000000fff00000,0x0000000100000000)
  to   space 1024K, 0% used [0x00000000ffe00000,0x00000000ffe00000,0x00000000fff00000)
 ParOldGen       total 10240K, used 4096K [0x00000000fec00000, 0x00000000ff600000, 0x00000000ff600000)
  object space 10240K, 40% used [0x00000000fec00000,0x00000000ff000010,0x00000000ff600000)
 Metaspace       used 2464K, capacity 4486K, committed 4864K, reserved 1056768K
  class space    used 265K, capacity 386K, committed 512K, reserved 1048576K

显然大数组4M直接进入Tenured而其他三个2M分配给 Young 给了我们上面的结果。

我怀疑其中大部分可以通过您的 JVM 中的 "other things" 来解释。

输出的最后一行证明您有一个代理连接到您的 JVM。

Disconnected from the target VM, address: '127.0.0.1:38815', transport: 'socket'

AFAIK,代理将导致 JVM 加载、编译等 类 您的应用程序不直接使用。然后代理将为套接字、缓冲区等分配堆对象以处理监控。

这将增加常规堆和 metaspace 的使用。

我建议您在不附加代理的情况下从 shell / 命令行重复此实验。通过命令行选项打开 GC 日志记录来获取 GC 统计信息。


你的更新。

Apparently the big array 4M is directly into the Tenured while the other three 2M allocated in Young giving us the above results.

正确。有一个阈值,高于该阈值的对象将直接分配到 tenured space。可以调....