JVM对象创建与内存分配

JVM对象创建与内存分配

前言

在我们创建对象时的一个流程是怎样的,创建的对象又应该在哪里分配给他内存,下面让我们一起来看一下吧。

03JVM对象创建与内存分配机制深度剖析.png

对象创建的流程

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

未命名文件 (12).png

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

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

    • 指针碰撞:适用于内存是规整的,也就是说已分配与未分配是分开的,在分配内存时,顺序分配下去即可。
    • 空闲列表:适用于内存是不规整的,内存布局比较分散,空闲内存会有一个列表进行维护,分配内存时在列表中找出合适的内存分配即可。

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

    • CAS(compare and swap):CAS会保证分配内存的原子性,给对象分配内存之前先看看这个内存跟以前是否一样,一样就分配,不一样就说明被分配出去了。
    • 本地线程缓冲栈:给每个线程分配一小块区域,每个线程只能在自己的区域里面分配内存。
  • 赋默认值:内存分配完成后,虚拟机需要将分配到的内存空间都初始化为零值。

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

32位对象头

clipboard.png

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

对象内存分配

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

未命名文件 (13).png

  • 逃逸分析与栈中分配:一般来说,对象通常是分配在堆中的,但是也有例外的情况,那就是产生了逃逸分析,什么是逃逸分析呢,一个对象在方法中被定义后,永远不会被其他外部方法所引用就是产生了逃逸,对于产生逃逸的对象我们没有必要在堆中给他分配内存,直接在栈中只为它的成员变量分配内存即可,也就是栈中分配,因为不需要被引用。例如下面这个对象:
public void test() {
   User user = new User();
   user.setId(1);
   user.setName("zhuge");
}
复制代码

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

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

    • 大对象直接进入老年代
    • 长期存活的对象将进入老年代
    • 对象动态年龄判断,一批对象的总大小大于这块Survivor区域内存大小的50%,那么此时大于等于这批对象年龄最大值的对象,就可以直接进入老年代了。
  • 对象在Eden区分配:大多数情况下,对象在新生代中 Eden 区分配。

おすすめ

転載: juejin.im/post/7035413431884709901