关于Java创建对象时虚拟机经历了什么

    Java是一门面向对象的编程语言,所以在程序运行时会不停地创建对象,编写程序时,仅仅一个new关键字就无需管其他的操作了,而虚拟机在同时做了很多事,主要经历了以下三个步骤(不包括数组和Class对象等):

     1、虚拟机会首先检查这条new指令的参数能不能在常量池中定位到一个类的符号引用,并且检查一下这个类有没有被加载、解析和初始化过。如果没有的话必须先执行类的加载。                                                                                                                             

常量池(Constant Pool Table):属于方法区中运行时常量池的一部分,当类加载后,常量池用来存放编译期生成的各种字面量和符号引用。

符号引用(Symbolic Referes):它是常量池中的一类常量,在这里只需知道它又包括了三类常量:类和接口的全限定名、字段的名称和描述符、方法的名称和描述符。通俗一点说就是,编译期只能加载一个类,因为没有真实存在的内存,所以给它一个唯一的代号(其实就是符号引用),保证这个代号能找到这个类,运行期的时候,再把这个类解析到具体的内存地址中,现在便有了直接引用。(姑且可以理解成创建对象就是将类的符号引用变为直接引用)

    2、为新生的对象分配内存,所需内存的大小在加载类的时候就完全确定了,也就是说现在需要从Java堆中划分出一块已知大小的内存出来,如何划分?根据Java堆是否规整,有两种分配方式:指针碰撞和空闲列表。

指针碰撞(Bump the Pointer):用过的内存放在一边,空闲的内存放在一边,中间放一个指针作为分界点的指示器,很显然,分配内存就是把这个指针往空闲的那一边挪动和对象大小的相等的一段距离。

空闲列表(Free List):如果堆中的内存不规整,用过的内存空间和空闲的内存空间交叉在一起,那虚拟机就需要维护一个列表,记录哪些内存块是可用的 ,分配内存就是在列表中找一个合适的空间给对象的实例。 

    由于对象创建频繁,所以在修改中间指针位置的时候,要考虑并发安全问题。两种方案解决此问题:一种是对分配内存的过程上锁;第二种就是把内存分配的动作按照线程划分在不同的空间进行,每个线程在Java堆中预先分配一小块内存,称为本地线程分配缓冲(TLAB)。哪个线程需要分配内存,就从这个线程的TLAB中分配,TLAB分配完时,再通过同步锁将给线程分配内存的过程锁起来就 OK啦。

    3、内存分配完后,虚拟机将分配到的内存空间初始化为零值,如果使用了TLAB,这个过程就在分配TLAB时进行。主要是为了保证对象的实例在不赋初始值就能直接使用,程序能访问到这些字段的数据类型所对应的零值。

    4、对象分配完内存之后,虚拟机要对对象进行一些设置,这个对象是哪个类的实例、如何找到元数据信息等等,这些信息在对象的对象头(Object Header)中 ,根据不同的运行状态,对象头也会有不同的设置。

    5、最后,一般来说在new之后都会执行<init>方法,也叫实例构造器,就是将对象按照程序员的意愿进行初始化,这个方法中的顺序如下:

    1. 类变量初始化 
    2. 父类语句块 
    3. 父类构造函数 
    4. 子类变量初始化 
    5. 子类语句块 
    6. 子类构造函数

对象创建完成!

第一篇博客,也是为了给自己捋一个思路,主要参考《深入理解Java虚拟机》

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值