🔉引言
我们说java
技术的内存管理无非就是解决两件事情:对象的内存分配和回收,关于回收我已经介绍了大量的篇幅,所以我们来聊聊前面没有提到的对象的内存分配。
对象的分配一般都是分配在堆中的,按照分代理论,新对象被分配在新生代,少数情况下,也可能直接分配在老年代,对象的分配规则并不是确定的,《java
虚拟机规范》中也没有给出具体的规则,这取决于我们使用哪款垃圾收集器。
对象的分配
对象优先在Eden
区分配,那Eden
区要是没有空间呢?就会触发一次MinorGC
。下面我将实战解析一下分配过程:
实战GC方式
那在讲解具体的分配过程之前,我们先偷瞄一下JDK
的默认GC
方式,通过java -XX:+PrintCommandLineFlags -version
命令查询,如下图:
可以非常明显的看出,实验用的是UseParallelGC
,我就不改了,大家要改的话可以改,比如我们要改Serial
,那就-XX:+UseSerialGC
配置。
实战代码
然后我搞了一段代码,搞了3
个2MB
对象和1
个4MB
的对象,别问为什么,因为心情。
package jvm;
public class TestAlloCation {
private static final int _1MB = 1024 * 1024;
public static void main(String[] args) {
byte[] bytes1 = new byte[_1MB * 2];
byte[] bytes2 = new byte[_1MB * 2];
byte[] bytes3 = new byte[_1MB * 2];
byte[] bytes4 = new byte[_1MB * 4];
System.out.println(bytes1);
System.out.println(bytes2);
System.out.println(bytes3);
System.out.println(bytes4);
}
}
参数设置
之后,我们来配置虚拟机参数,没有参数的虚拟机那能叫虚拟机嘛?
我们来分析一下这些参数,作用如下:
- 指定堆的大小是20M,而且是不可变的
- 打印GC详情,要不我们看啥呢?
- 指定新生代的Eden和Survivor区的比列:8比1
日志分析
eden space 8192K, 78% used from space 1024K, 88% used to pace 1024K
,确实是8
比1
,新生代可用空间为9126k
,分配失败了,分配第四个对象的时候发现空间不够4M
,发生了一次MinorGC
,使新生代由[PSYoungGen: 6804K->959K(9216K)],但总内存并没有减少,原因是前三个对象依然是存活对象,eden区位置不够,Survivor只有1M,也不够,故把Eden
区的三个对象分配到老年代,图如下。
日志分析纠错
那按理说:老年代应该有6M
啊,怎么才4M
呢?
我后来经过10
分钟的思考,发现多了1M
是我们定义的成员变量,我们来重新梳理一下:1M的成员和2
个2M
的对象分配进去,此时,Eden
已经是6M
多了,大概是6742k
,那还剩2474k
,由于还得预留一部分空间,已经不够玩的了,就发生第一次MinorGC
,[PSYoungGen: 6578K->968K(9216K)],然后在把第三个对象分配进去,内存占有是3182K
,把剩余的4M
对象放到老年代,老年代内存占用就是4104K
,再来一个对象,新生代就是7369K
了,也就对应我们分析之前的场景。
图如下:
📝题外话
当在一个现象综合体中起作用的因素太多时,绝大多数情况下,科学方法是不起作用的。人们只要想想天气就知道了,哪怕只是对几天之后的天气进行预报也不可能。然而没有人怀疑我们正面临一种因果联系,其中构成原因的成分大体上已为我们所知。人们不能对这个领域发生的事情进行精确的预测,是因为起作用的因素具有多样性,而不是因为自然界中缺乏秩序。