JVM二:对象的创建(详解new后发生的故事)

在Java中我们创建对象都会用new进行创建,下面我来接收一下new之后对象创建及内存分配的具体的过程

一:虚拟机遇到一条new指令后,先去检查这条指令参数是否能在常量池中定位到一个类的符号引用,并检查这个符号引用代表的类是否已被加载、解析和初始化过,如果没有,那必须先执行相应的类加载过程。

二:类加载检查通过后,接下来虚拟机为新生对象分配内存,因为对象所需内存的大小在类加载后是完全确定的(用引用,堆中存放实例),所以只需分配一个固定大小的内存即可。分配内存的方法用两种(Java虚拟机中)

①指针碰撞:假设Java堆中内存是绝对规整的,所有用过的内存都存放在一边,空闲的内存存在在另一边,中间放着一个以指针为分界点的指示器,分配内存就是把指针往空闲的那侧移动对象需要的内存即可。但是如果Java内存堆不是完整的,就没有办法进行简单的指针碰撞,

②空闲列表:空闲列表就是虚拟机先对内存分块,并用一个表记录每个内存块是否使用的情况,为对象分配内存时只需更新列表上的记录即可,选择哪一种分配方式取决于对中的内存使用情况是否完整。

但是在为对象分配内存的同时,需要考虑其它线程是否也在这一时刻需要分配,因此需要考虑到线程的安全问题,通过以下两种方法进行解决:

①同步处理:一种是对分配内存的空间动作进行同步处理--实际上虚拟机采用CAS配上失败重试的方式保证更新操作的原子性

②本地线程分配缓存TLAB:另一种是把内存分配的动作按照线程在不同的空间之中进行,及每个线程先预先分配一定的缓存。

三:内存分配完成后,虚拟机将分配到的内存空间都初始化为零值(不包括对象头),如果采用的是TLAB的方法分配,则这一步骤在分配之前完成。这一步操作保证了对象的实例字段在Java代码中可以不付初始值就直接使用,程序访问的值为false。这个解决了以前我在上Java课与老师争论在执行构造方法是否会先初始化,明显我对了,但是当时没有拿出有说服力的证据。

四:在上面的步骤完成后,从虚拟机的视角,一个新的对象已经产生了,但是从Java程序的视角来看,对象创建方法才刚刚开始,还要执行对象的init方法(构造方法),一个对象才完成创建

以下是HotSpot解释器的代码片段(C++)

// 确保常量池中存放的是已解释的类
if (!constants->tag_at(index).is_unresolved_klass()) {
  // 断言确保是klassOop和instanceKlassOop(这部分下一节介绍)
  oop entry = (klassOop) *constants->obj_at_addr(index);
  assert(entry->is_klass(), "Should be resolved klass");
  klassOop k_entry = (klassOop) entry;
  assert(k_entry->klass_part()->oop_is_instance(), "Should be instanceKlass");
  instanceKlass* ik = (instanceKlass*) k_entry->klass_part();
  // 确保对象所属类型已经经过初始化阶段
  if ( ik->is_initialized() && ik->can_be_fastpath_allocated() ) {
    // 取对象长度
    size_t obj_size = ik->size_helper();
    oop result = NULL;
    // 记录是否需要将对象所有字段置零值
    bool need_zero = !ZeroTLAB;
    // 是否在TLAB中分配对象
    if (UseTLAB) {
      result = (oop) THREAD->tlab().allocate(obj_size);
    }
    if (result == NULL) {
      need_zero = true;
      // 直接在eden中分配对象
retry:
      HeapWord* compare_to = *Universe::heap()->top_addr();
      HeapWord* new_top = compare_to + obj_size;
      // cmpxchg是x86中的CAS指令,这里是一个C++方法,通过CAS方式分配空间,并发失败的话,转到retry中重试直至成功分配为止
      if (new_top <= *Universe::heap()->end_addr()) {
        if (Atomic::cmpxchg_ptr(new_top, Universe::heap()->top_addr(), compare_to) != compare_to) {
          goto retry;
        }
        result = (oop) compare_to;
      }
    }
    if (result != NULL) {
      // 如果需要,为对象初始化零值
      if (need_zero ) {
        HeapWord* to_zero = (HeapWord*) result + sizeof(oopDesc) / oopSize;
        obj_size -= sizeof(oopDesc) / oopSize;
        if (obj_size > 0 ) {
          memset(to_zero, 0, obj_size * HeapWordSize);
        }
      }
      // 根据是否启用偏向锁,设置对象头信息
      if (UseBiasedLocking) {
        result->set_mark(ik->prototype_header());
      } else {
        result->set_mark(markOopDesc::prototype());
      }
      result->set_klass_gap(0);
      result->set_klass(k_entry);
      // 将对象引用入栈,继续执行下一条指令
      SET_STACK_OBJECT(result, 0);
      UPDATE_PC_AND_TOS_AND_CONTINUE(3, 1);
    }
  }
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值