之前读第二版的时候,当时测试的时候就发现结果与书中不一致,第二版基于JDK7 ,本人是JDK8,以下为书中的结果:
private static final int _1MB = 1024 * 1024;
/**
* VM参数:-verbose:gc -Xms20M -Xmx20M -Xmn10M -XX:+PrintGCDetails -XX:SurvivorRatio=8
*/
public static void testAllocation() {
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]; // 出现一次Minor GC
}
本人在测试时发现的结果:
一开始看到老年代占了40%,我以为数组4直接进入进入老年代,毕竟也算大对象,经过几次实验得出结论,在分配数组3时就已经触发GC了,老年代中的40%是数组1和数组2.
debug发现在分配数组3是就已经触发GC了
此外 我们还可以更改数组1或数组4的大小进行验证 ,首先把4改为5MB,我们可以发现老年代中的大小并未改变,而E区的从78%->90%,验证了我们的结论。
也可以改数组1、2的大小,老年代的占比就会发生改变,不在贴图,可自己验证。
同时还有个问题 S1区的94%究竟放的是谁呢?数组的大小最低都是2MB,而S1区的空间只有1024K。同时S1区也是GC后存活的对象待的地方,那么它可能也就是引起提前GC的原因。
在我把里面所有的方法变量都注释后,还是会占28%的空间,
一个可能的答案:仅供参考。
Java中的main方法本身并不会占用2MB大小的Eden区空间。然而,Java虚拟机在启动时需要对一些类进行加载和初始化,这些操作可能会占用一定的内存空间。
当我们在命令行中执行java命令来启动一个Java程序时,Java虚拟机会先创建一个进程,并为该进程分配一定的内存空间。其中,包括了用于存放程序代码和数据的堆内存(Heap)以及用于存储Java虚拟机本身运行所需数据的非堆内存(Non-Heap)。
在堆内存中,Java虚拟机会将内存划分为多个区域,其中包括了年轻代(Young Generation),年老代(Old Generation)等。年轻代又会被划分为Eden区、Survivor区1和Survivor区2。而在默认情况下,Java虚拟机会将Eden区的大小设置为2MB。
当我们执行java命令启动Java程序时,Java虚拟机会首先加载并初始化一些必要的类,例如java.lang.Object、java.lang.String等。这些类的大小通常较大,因此可能会占用一部分Eden区的空间。此外,Java虚拟机还会为线程、类加载器等对象分配一些内存空间,这也会占用一定的Eden区空间。
因此,尽管main方法本身并不会占用2MB大小的Eden区空间,但是Java虚拟机在启动时所做的一些操作可能会占用一部分内存空间,包括Eden区的空间。
有了解的朋友欢迎探讨。