1)java虚拟机中对象的创建

  我们了解虚拟机内存划分的人,都知道对象的内存分配几乎都是在堆上的,这一点在java虚拟机规范中的描述是:所有的对象实例以及数组都会在堆上分配(但是随着JIT编译器的发展与逃逸分析技术逐渐成熟,栈上分配,标量替换优化技术将会导致一些变化,擦扯远了,所以所有的对象实例都在堆上分配就不是那么绝对了)

下面说虚拟机中对象的创建几个步骤

  1. 类加载检查
      虚拟机遇到new指令时,首先会去检查这个指令的参数是否能在常量池中定位到一个类的符号引用,并且检查这个符号引用代表的类是否被装载、解析和初始化过。如果没有那必须先执行相应的类的加载过程。
  2. 为对象分配内存
      当类加载成功后,类的对象的大小是确定了的。对象内存的划分等同于在堆里划分出一块指定大小的内存。
      假设内存是规整的,所有用过的内存放在一边,空闲的放在另一边,中间分界点有个指针,那分配内存就是把指针向空闲区域方向挪动一段于对象大小相等的长度,这种分配方式叫做“指针碰撞”(Bump the Pointer)。
      如果内存不是规整的,那么就需要一个表来记录,记录哪些内存是占用的,哪些是空闲的,那分配内存就是在表里找到一块足够大的空间分配给对象实例,并更新这个表的记录,这种分配方式叫做“空闲列表”(Free List)。
      选择哪种分配方式与堆是否规整决定,而java堆是否规整又和垃圾收集器是否有压缩整理功能决定。因此,在使用Serial、ParNew等带有Compact过程的收集器时,采用的分配方式是指针碰撞,而使用CMS这种基于Mark-Sweep算法的收集器时,采用的是空闲列表。
      除了如何划分内存之外还有一个是我们需要考虑的问题,因为java堆是线程共享的,那么多个线程同时操作堆上的内存就会有问题,比如正在给a对象分配内存还没来得及移动指针(或者是没有修改空闲列表的记录),这时对象b使用原来的指针来分配内存,就会产生问题。解决这个问题有两种方式,一种是对分配内存的动作进行同步处理(实际上虚拟机采用CAS配上错误重试来保证分配内存的原子性);另一种是采用线程间不共享的内存来分配,每个线程预先在java堆中分配一块内存,成为本地线程分配缓冲(Thread Local Allocation Buffer,TLAB )。哪个线程分配内存就在该线程的TLAB上分配,只有在TLAB用完需要分配新的TLAB时,才需要同步锁定。注:虚拟机是否使用TLAB,可以通过-XX:+/-UseTLAB参数来设定。
  3. 内存初始化
      内存分配完后,需要把将分配到的内存空间初始化为零值(不包换对象头),如果使用TLAB这一过程也可以提前至TLAB分配时进行。这一步操作保证了对象实例字段在java代码中可以不赋初值就使用,程序能访问到这些字段的数据类型所对应的零值。
  4. 对象头必要设置
      接下来虚拟机要对对象进行必要的设置,比如该对象是哪个类的实例、如何才能找到类的元数据信息、对象的hash码、对象的gc分代年龄等信息。这些信息均放在对象的对象头(Object Header)中。根据虚拟机当前的运行状态的不同,如是否启用偏向锁等,对象头会有不同的设置

总结
以上步骤完成后,从虚拟机的角度来看,一个新对象诞生了,但从java程序角度看,一切才刚刚开始——init方法还没有执行,所有字段的数据类型都是对应的零值。所以一般来说执行new指令后会接着执行init方法,把对象按照程序猿的意愿进行初始化,这样一个真正可以使用的对象才算完成。

下面的代码是HotPot虚拟机bytecodeInterpreter.cpp的代码片段

u2 index = Bytes::get_Java_u2(pc+1);
ConstantPool* constants = istate->method()->constants();
// 确保常量池中是已经解释的类
if (!constants->tag_at(index).is_unresolved_klass()) {
  // Make sure klass is initialized and doesn't have a finalizer
  // 确保类已经初始化
  Klass* entry = constants->slot_at(index).get_klass();
  assert(entry->is_klass(), "Should be resolved klass");
  Klass* k_entry = (Klass*) entry;
  assert(k_entry->oop_is_instance(), "Should be InstanceKlass");
  InstanceKlass* ik = (InstanceKlass*) k_entry;
  // 确保对象内存已经初始化
  if ( ik->is_initialized() && ik->can_be_fastpath_allocated() ) {
    // 取对象长度
    size_t obj_size = ik->size_helper();
    oop result = NULL;
    // If the TLAB isn't pre-zeroed then we'll have to do it
    // 如果TLAB没有预先初始化那么need_zero为true后边会进行初始化
    bool need_zero = !ZeroTLAB;
    // 如果虚拟机启用TLAB,那么在TLAB中分配对象
    if (UseTLAB) {
      result = (oop) THREAD->tlab().allocate(obj_size);
    }
    if (result == NULL) {
      need_zero = true;
      // Try allocate in shared eden
      // 尝试在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) {
      // Initialize object (if nonzero size and need) and then the header
     // 如果需要,则为对象初始化零值
      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);
    }
  }
}

相关文章:
java虚拟机中对象的创建
java虚拟机中对象的内存布局
java虚拟机中对象的定位

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

推荐阅读更多精彩内容