• 让天下没有难学的技术
    多数学员都来自推荐,这就是口碑的力量

JVM对象创建与内存分配

对象创建的流程

在我们创建一个对象时,主要经历以下几个阶段:

类加载检查:在分配内存之前,类必须要先加载,因此进行类加载检查。类加载就是将字节码文件读入到JVM。

分配内存:一般对象都是在堆中分配内存。那么内存是如何分配的,有两种分配内存的方法:

指针碰撞:适用于内存是规整的,也就是说已分配与未分配是分开的,在分配内存时,顺序分配下去即可。

空闲列表:适用于内存是不规整的,内存布局比较分散,空闲内存会有一个列表进行维护,分配内存时在列表中找出合适的内存分配即可。

以上分配方式在并发环境下,会出现一些问题,试想一下,在同一时间恰有多个对象指向了同一块内存,这块内存到底改分配给谁?针对上述问题,我们也有解决方案:

CAS(compare and swap):CAS会保证分配内存的原子性,给对象分配内存之前先看看这个内存跟以前是否一样,一样就分配,不一样就说明被分配出去了。

本地线程缓冲栈:给每个线程分配一小块区域,每个线程只能在自己的区域里面分配内存。

赋默认值:内存分配完成后,虚拟机需要将分配到的内存空间都初始化为零值。

设置对象头:一个对象主要由对象头(Header)、 实例数据(Instance Data)和对齐填充(Padding)组成,对象头主要结构如下:

32位对象头

执行init方法:为属性赋予真正的值。

对象内存分配

说完对象创建的流程,下面我们一起看一下对象是如何进行内存分配的。

逃逸分析与栈中分配:一般来说,对象通常是分配在堆中的,但是也有例外的情况,那就是产生了逃逸分析,什么是逃逸分析呢,一个对象在方法中被定义后,永远不会被其他外部方法所引用就是产生了逃逸,对于产生逃逸的对象我们没有必要在堆中给他分配内存,直接在栈中只为它的成员变量分配内存即可,也就是栈中分配,因为不需要被引用。例如下面这个对象:

public void test() {

User user = new User();

user.setId(1);

user.setName(“zhuge”);

}

User就产生了逃逸,因为在这个方法中没有返回任何对象,所以User不会被其他外部对象引用。

对象直接进入老年代:让对象直接进入老年代只有一个目的,为了避免为大对象分配内存时的复制操作而降低效率。对象在以下几种情况会直接进入老年代。

大对象直接进入老年代

长期存活的对象将进入老年代

对象动态年龄判断,一批对象的总大小大于这块Survivor区域内存大小的50%,那么此时大于等于这批对象年龄最大值的对象,就可以直接进入老年代了。

对象在Eden区分配:大多数情况下,对象在新生代中 Eden 区分配。

发表评论

您的电子邮箱地址不会被公开。 必填项已用*标注