JVM中对象的创建过程

参考书籍:《深入理解Java虚拟机》

参考博客:http://blog.csdn.net/tjiyu/article/details/53923392

下面我们详细了解Java程序中new一个普通对象时,HotSpot虚拟机是怎么样创建这个对象的,包括5个步骤:相应类加载检查过程、在Java堆中为对象分配内存、分配后内存初始化为零、对对象进行必要的设置、以及执行对象实例方法<init>,最后我们再从JVM指令角度来解释下Java对象创建。

   1、相应类加载检查过程

 通过《JVM字节码指令及反编译分析》可以知道:Java程序中的“new”操作会转换为Class文件中方法的“new”字节指令。JVM(本文特指HotSpot)遇到new指令时,先检查指令参数是否能在常量池中定位到一个类的符号引用

 (A)、如果能定位到,检查这个符号引用代表的类是否已被加载、解析和初始化过;

 (B)、如果不能定位到,或没有检查到,就先执行相应的类加载过程;

2、为对象分配内存

   对象所需内存的大小在类加载完成后便完全确定(JVM可以通过普通Java对象的类元数据信息确定对象大小);

  为对象分配内存相当于把一块确定大小的内存从Java堆里划分出来;

(A)、分配方式:

(I)、指针碰撞

      如果Java堆是绝对规整的:一边是用过的内存,一边是空闲的内存,中间一个指针作为边界指示器;

      分配内存只需向空闲那边移动指针,这种分配方式称为"指针碰撞"(Bump the Pointer);

(II)、空闲列表

      如果Java堆不是规整的:用过的和空闲的内存相互交错;

      需要维护一个列表,记录哪些内存可用;

      分配内存时查表找到一个足够大的内存,并更新列表,这种分配方式称为"空闲列表"(Free List);

      Java堆是否规整由JVM采用的垃圾收集器是否带有压缩功能决定的。 所以,使用Serial、ParNew等带Compact过程的收集器时,JVM       采用指针碰撞方式分配内存;而使用CMS这种基于标记-清除(Mark-Sweep)算法的收集器时,采用空闲列表方式;后面再介绍垃圾       收集算法和垃圾收集器,了解垃圾收集时应注意这里的内容;

(B)、线程安全问题

      并发时,上面两种方式分配内存的操作都不是线程安全的,有两种解决方案:

(I)、同步处理

      对分配内存的动作进行同步处理:

      JVM采用CAS(Compare and Swap)机制加上失败重试的方式,保证更新操作的原子性;

      CAS:有3个操作数,内存值V,旧的预期值A,要修改的新值B。当且仅当预期值A和内存值V相同时,将内存值V修改为B,否则什么都不做;

(II)、本地线程分配缓冲区

      把分配内存的动作按照线程划分在不同的空间中进行:

      在每个线程在Java堆预先分配一小块内存,称为本地线程分配缓冲区(Thread Local Allocation Buffer,TLAB);

      哪个线程需要分配内存就从哪个线程的TLAB上分配;

      只有TLAB用完需要分配新的TLAB时,才需要同步处理;

JVM通过"-XX:+/-UseTLAB"指定是否使用TLAB;

3、对象内存初始化为零

 对象内存初始化为零,但不包括对象头; 如果使用TLAB,提前至分配TLAB时;这保证了程序中对象(及实例变量)不显式初始赋零值,程序也能访问到零值;

4、对象内存初始化为零

 主要设置对象头信息,包括类元数据引用、对象的哈希码、对象的GC分代年龄等(详见下节);

5、执行对象实例方法<init>

该方法把对象(实例变量)按照程序中定义的初始赋值进行初始化; 通常,经过上面5步对象才完全new出来。另外,还可以参考HotSpot虚拟机源码中的"bytecodeInterpreter.cpp"文件,这个文件有表示解释器处理"new"指令基本类似上面5个过程。

6、Java对象创建的JVM指令

  通过前面一些文章,我们还可以从JVM指令的角度来看对象的创建过程:

(A)、new指令

         "new"指令有一个类符号引用的常量,JVM解析该常量也就对应步骤1"相应类加载检查过程";

         "new"指令执行完毕后,一个代表(指向)该对象实例内存数据的reference类型变量数据将压入到操作数栈中;

(B)、dup指令

         接着会执行"dup"指令复制该reference数据,这时操作数栈栈顶就有两个指向该对象实例内存的reference数据;              (如果             <init>方法有参数,还需要把参数加载到操作栈)

(C)、invokespecial指令

         再执行"invokespecial"指令调用对象实例方法<init>,这时操作数栈最上面的一个reference数据会出栈(如果有参数,包括方法参           数); 然后在Java虚拟机栈中创建<init>方法的栈帧,把出栈的reference数据(和参数)放入该栈帧的局部变量表中,该reference         数据在方法中也就是"this",表示对该对象实例进行的操作: 当然这些参数的数值、数据类型和顺序都必须遵循实例方法的描述符中         的描述;另外,操作数栈中还有一个对象reference数据一般被“astore”到局部变量表或保存到字段变量,给后面访问对象使用。

 

 到这里,我们大体了解Java对象在HotSpot虚拟机中的创建过程, 后面我们将分别去了解:对象的内存布局、对象的访问定位、方法的  调用与执行、JIT编译、以及JVM垃圾收集相关内容……


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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值