【jvm学习笔记二】Java对象的创建过程

上文分析了Java类的加载过程,那么Java对象的创建过程是什么样呢?

public class Test {
    public static void main(String[] args) {
       Test data = new Test();
    }
}

以上为例,看下new关键字在底层是怎么实现的。
new关键字实现在InterpreterRuntime类中,如下:

IRT_ENTRY(void, InterpreterRuntime::_new(JavaThread* thread, ConstantPool* pool, int index))
  Klass* k_oop = pool->klass_at(index, CHECK);
  instanceKlassHandle klass (THREAD, k_oop);

  // Make sure we are not instantiating an abstract klass
  klass->check_valid_for_instantiation(true, CHECK);

  // Make sure klass is initialized
  klass->initialize(CHECK);
  //分配实例
  oop obj = klass->allocate_instance(CHECK);
  thread->set_vm_result(obj);
IRT_END

主要分为四步:

  1. 从常量池pool中获取Klass,如果已加载并符号解析,则直接返回,否则根据情况符号解析或加载并符号解析;
  2. 检查是否是抽象类
  3. 初始化
  4. 分配实例创建对象

第一步的实现方法如下:

Klass* ConstantPool::klass_at_impl(constantPoolHandle this_oop, int which, TRAPS) {
  
  CPSlot entry = this_oop->slot_at(which);
  //如果要new的类已经加载并解析过(符号解析),则直接返回
  if (entry.is_resolved()) {
    assert(entry.get_klass()->is_klass(), "must be");
    // Already resolved - return entry.
    return entry.get_klass();
  }

  // 其他略

  Symbol* name = NULL;
  Handle       loader;
  {  MonitorLockerEx ml(this_oop->lock());

    if (this_oop->tag_at(which).is_unresolved_klass()) {
      if (this_oop->tag_at(which).is_unresolved_klass_in_error()) {
        in_error = true;
      } else {
        //从常量池中获取符合引用,类加载器
        do_resolve = true;
        name   = this_oop->unresolved_klass_at(which);
        loader = Handle(THREAD, this_oop->pool_holder()->class_loader());
      }
    }
  } // unlocking constantPool


 //错误处理略

  if (do_resolve) {
    // this_oop must be unlocked during resolve_or_fail
    oop protection_domain = this_oop->pool_holder()->protdomain();
    Handle h_prot (THREAD, protection_domain);
    //从SystemDictionary获取已加载的KlassOop,没有加载则加载
    Klass* k_oop = SystemDictionary::resolve_or_fail(name, loader, h_prot, true, THREAD);
    KlassHandle k;
    if (!HAS_PENDING_EXCEPTION) {
      k = KlassHandle(THREAD, k_oop);
      // preserve the resolved klass.
      mirror_handle = Handle(THREAD, k_oop->java_mirror());
      // Do access check for klasses
      verify_constant_pool_resolve(this_oop, k, THREAD);
    }

    // 失败处理略
    }

    if (TraceClassResolution && !k()->oop_is_array()) {
        略

    } else {
      //符合引用变成直接引用,并更新到常量池中
      MonitorLockerEx ml(this_oop->lock());
      // Only updated constant pool - if it is resolved.
      do_resolve = this_oop->tag_at(which).is_unresolved_klass();
      if (do_resolve) {
        ClassLoaderData* this_key = this_oop->pool_holder()->class_loader_data();
        this_key->record_dependency(k(), CHECK_NULL); // Can throw OOM
        this_oop->klass_at_put(which, k());
      }
    }
  }

  entry = this_oop->resolved_klass_at(which);
  assert(entry.is_resolved() && entry.get_klass()->is_klass(), "must be resolved at this point");
  return entry.get_klass();
}

第三步初始化是重点:

void InstanceKlass::initialize_impl(instanceKlassHandle this_oop, TRAPS) {
  // Make sure klass is linked (verified) before initialization
  // A class could already be verified, since it has been reflected upon.
  // 如果没有链接(验证、准备、解析)过么,则进行链接
  this_oop->link_class(CHECK);

  DTRACE_CLASSINIT_PROBE(required, InstanceKlass::cast(this_oop()), -1);

  bool wait = false;

  // refer to the JVM book page 47 for description of steps
  // 以下步骤在《虚拟机规范》中的初始化章节有定义
  // Step 1
  //获取初始化锁LC(每个类初始化锁唯一),没有则等待
  {
    oop init_lock = this_oop->init_lock();
    ObjectLocker ol(init_lock, THREAD, init_lock != NULL);

    Thread *self = THREAD; // it's passed the current thread

    // Step 2
    // 如果其他线程正在初始化,则释放LC并等待其他线程初始化完成,执行初始化过程不会被中断。
    // If we were to use wait() instead of waitInterruptibly() then
    // we might end up throwing IE from link/symbol resolution sites
    // that aren't expected to throw.  This would wreak havoc.  See 6320309.
    while(this_oop->is_being_initialized() && !this_oop->is_reentrant_initialization(self)) {
        wait = true;
      ol.waitUninterruptibly(CHECK);
    }

    // Step 3
    // 如果当前线程正在初始化,表明这是对初始化的递归请求(例:A的静态属性创建了B,B的静态属性又创建A),释放LC并返回
    if (this_oop->is_being_initialized() && this_oop->is_reentrant_initialization(self)) {
      DTRACE_CLASSINIT_PROBE_WAIT(recursive, InstanceKlass::cast(this_oop()), -1,wait);
      return;
    }

    // Step 4
    // 如果已经初始化完成,则释放LC并返回
    if (this_oop->is_initialized()) {
      DTRACE_CLASSINIT_PROBE_WAIT(concurrent, InstanceKlass::cast(this_oop()), -1,wait);
      return;
    }

    // Step 5
    //如果初始化发生错误,则抛出NoClassDefFoundError异常
    if (this_oop->is_in_error_state()) {
      DTRACE_CLASSINIT_PROBE_WAIT(erroneous, InstanceKlass::cast(this_oop()), -1,wait);
      ResourceMark rm(THREAD);
      const char* desc = "Could not initialize class ";
      const char* className = this_oop->external_name();
      size_t msglen = strlen(desc) + strlen(className) + 1;
      char* message = NEW_RESOURCE_ARRAY(char, msglen);
      if (NULL == message) {
        // Out of memory: can't create detailed error message
        THROW_MSG(vmSymbols::java_lang_NoClassDefFoundError(), className);
      } else {
        jio_snprintf(message, msglen, "%s%s", desc, className);
        THROW_MSG(vmSymbols::java_lang_NoClassDefFoundError(), message);
      }
    }

    // Step 6
    // 记录下当前线程正在初始化的Class对象
    this_oop->set_init_state(being_initialized);
    this_oop->set_init_thread(self);
  }

  // Step 7 如果是类且不是接口,有父类还没有初始化,则触发父类的初始化
  Klass* super_klass = this_oop->super();
  if (super_klass != NULL && !this_oop->is_interface() && super_klass->should_be_initialized()) {
    super_klass->initialize(THREAD);

    if (HAS_PENDING_EXCEPTION) {
      Handle e(THREAD, PENDING_EXCEPTION);
      CLEAR_PENDING_EXCEPTION;
      {
        EXCEPTION_MARK;
        this_oop->set_initialization_state_and_notify(initialization_error, THREAD); // Locks object, set state, and notify all waiting threads
        CLEAR_PENDING_EXCEPTION;   // ignore any exception thrown, superclass initialization error is thrown below
      }
      DTRACE_CLASSINIT_PROBE_WAIT(super__failed, InstanceKlass::cast(this_oop()), -1,wait);
      THROW_OOP(e());
    }
  }

  // Recursively initialize any superinterfaces that declare default methods
  // Only need to recurse if has_default_methods which includes declaring and
  // inheriting default methods
  if (this_oop->has_default_methods()) {
    this_oop->initialize_super_interfaces(this_oop, CHECK);
  }
  
  // Step 8
  // 执行初始化方法,即clinit方法
  {
    assert(THREAD->is_Java_thread(), "non-JavaThread in initialize_impl");
    JavaThread* jt = (JavaThread*)THREAD;
    DTRACE_CLASSINIT_PROBE_WAIT(clinit, InstanceKlass::cast(this_oop()), -1,wait);
    // Timer includes any side effects of class initialization (resolution,
    // etc), but not recursive entry into call_class_initializer().
    PerfClassTraceTime timer(ClassLoader::perf_class_init_time(),
                             ClassLoader::perf_class_init_selftime(),
                             ClassLoader::perf_classes_inited(),
                             jt->get_thread_stat()->perf_recursion_counts_addr(),
                             jt->get_thread_stat()->perf_timers_addr(),
                             PerfClassTraceTime::CLASS_CLINIT);
    this_oop->call_class_initializer(THREAD);
  }

  // Step 9
  // 初始化执行成功,则标记为完全初始化,并通知其他等待的线程,退出。
  if (!HAS_PENDING_EXCEPTION) {
    this_oop->set_initialization_state_and_notify(fully_initialized, CHECK);
    { ResourceMark rm(THREAD);
      debug_only(this_oop->vtable()->verify(tty, true);)
    }
  }
  else {
    // Step 10 and 11
    // 发生异常且异常不是error或它子类,则构建Exception对象抛出
    Handle e(THREAD, PENDING_EXCEPTION);
    CLEAR_PENDING_EXCEPTION;
    {
      EXCEPTION_MARK;
      this_oop->set_initialization_state_and_notify(initialization_error, THREAD);
      CLEAR_PENDING_EXCEPTION;   // ignore any exception thrown, class initialization error is thrown below
    }
    DTRACE_CLASSINIT_PROBE_WAIT(error, InstanceKlass::cast(this_oop()), -1,wait);
    if (e->is_a(SystemDictionary::Error_klass())) {
      THROW_OOP(e());
    } else {
      JavaCallArguments args(e);
      THROW_ARG(vmSymbols::java_lang_ExceptionInInitializerError(),
                vmSymbols::throwable_void_signature(),
                &args);
    }
  }
  DTRACE_CLASSINIT_PROBE_WAIT(end, InstanceKlass::cast(this_oop()), -1,wait);
}

最后创建对象

instanceOop InstanceKlass::allocate_instance(TRAPS) {
  bool has_finalizer_flag = has_finalizer(); // Query before possible GC
  int size = size_helper();  // Query before forming handle.

  KlassHandle h_k(THREAD, this);

  instanceOop i;
  //创建实例
  i = (instanceOop)CollectedHeap::obj_allocate(h_k, size, CHECK_NULL);
  if (has_finalizer_flag && !RegisterFinalizersAtInit) {
    //注册finalizer
    i = register_finalizer(i, CHECK_NULL);
  }
  return i;
}

继续跟进obj_allocate:

oop CollectedHeap::obj_allocate(KlassHandle klass, int size, TRAPS) {
  //略
  //分配对象
  HeapWord* obj = common_mem_allocate_init(klass, size, CHECK_NULL);
  //对象初始化,根据是否使用偏向锁,设置对象头信息等操作。
  post_allocation_setup_obj(klass, obj, size);
  NOT_PRODUCT(Universe::heap()->check_for_bad_heap_word_value(obj, size));
  return (oop)obj;
}

继续跟进common_mem_allocate_noinit
···
HeapWord* CollectedHeap::common_mem_allocate_noinit(KlassHandle klass, size_t size, TRAPS) {

// 略
HeapWord* result = NULL;
if (UseTLAB) {
//先尝试快速分配(tlab分配,eden区中线程专属区域),失败则再尝试从tlab中慢速分配,再失败会从eden区共享区域分配
result = allocate_from_tlab(klass, THREAD, size);
if (result != NULL) {
assert(!HAS_PENDING_EXCEPTION,
"Unexpected exception, will result in uninitialized storage");
return result;
}
}
//略
···
从tlab中快速分配失败后(如空间不够),会进入allocate_from_tlab_slow方法:
···
HeapWord* CollectedHeap::allocate_from_tlab_slow(KlassHandle klass, Thread* thread, size_t size) {

// 如果tlab空间大无法discard(这个阈值由TLABRefillWasteFraction比例控制),则记录并返回null(后续会从eden区共享区域分配)
if (thread->tlab().free() > thread->tlab().refill_waste_limit()) {
thread->tlab().record_slow_allocation(size);
return NULL;
}

// 重新分配一个tlab,为了减少内存碎片,此TLAB可能比上一个小
size_t new_tlab_size = thread->tlab().compute_size(size);

thread->tlab().clear_before_allocation();

if (new_tlab_size == 0) {
return NULL;
}

// Allocate a new TLAB...
HeapWord* obj = Universe::heap()->allocate_new_tlab(new_tlab_size);
if (obj == NULL) {
return NULL;
}
//略
return obj;
}
···
实例空间分配完成后,会进行一些初始化操作:

void CollectedHeap::init_obj(HeapWord* obj, size_t size) {
  assert(obj != NULL, "cannot initialize NULL object");
  const size_t hs = oopDesc::header_size();
  assert(size >= hs, "unexpected object size");
  //将对象空间除对象头外填0处理
  ((oop)obj)->set_klass_gap(0);
  //先将地址类型转换,然后把堆的字数转化为字节数,再对该段内存进行填值
  Copy::fill_to_aligned_words(obj + hs, size - hs);
}

对象的初始化,对象头的填充等操作在post_allocation_setup_obj方法中。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 215,794评论 6 498
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,050评论 3 391
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 161,587评论 0 351
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,861评论 1 290
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,901评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,898评论 1 295
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,832评论 3 416
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,617评论 0 271
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,077评论 1 308
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,349评论 2 331
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,483评论 1 345
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,199评论 5 341
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,824评论 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,442评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,632评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,474评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,393评论 2 352

推荐阅读更多精彩内容