触发 : 程序建立对象,例如clone,反序列化,new等。java
验证类加载 : 当虚拟机接收到new指令时,检查指令的参数可否在常量池定位到一个类的符号引用,而且检查此符号引用的类是否已经被加载、解析、初始化过,若是没有,则先执行对应的初始化过程。程序员
分配内存空间 : 为新生代对象分配内存,所需内存在类加载完成后即可彻底肯定。分配内存空间即从堆中划分一块肯定大小的内存,此时分两种状况:安全
①堆内存规整,使用中内存与空闲内存被一个指针隔离在两边,此时只须要将指针向空闲空间方向挪动此对象大小距离便可,这种方式称为指针碰撞;并发
②若是内存不规整,使用中内存与空闲内存相互交错,此时虚拟机须要维护一个列表,记录哪些内存块可用,在分配空间时须要找到一块足够大的空间划分给对象实例,并更新维护的列表,这种方式为空闲列表。oop
选择哪一种分配方式由java堆是否规整决定,而java堆是否规整又由所采用的垃圾收集器是否带有压缩整理(compact)功能决定,例如Serial、PerNew等使用指针碰撞,CMS基于Mark-Sweep采用空闲列表。
因为对象建立对象很是的频繁,在并发状况下不少操做都不是线程安全的,例如修改一个指针所指向的位置,可能为A分配内容时还没来得及修改指针,对象B又引用了此指针位置为碰撞点来划份内存,两种解决方案:布局
零值初始化 : 内存分配完成后,虚拟机须要将分配到的内存空间都初始化为零值(不包含对象头),这一步操做保证了对象的实例字段在java代码中能够不赋值就直接使用,程序能访问到这些字段的数据类型所对应的零值。性能
设置对象头 : 对对应对象头进行设置必要设置,例如这个对象属于哪一个类,若是找到类的元数据,对象的哈希码,对象的gc分代年龄等信息,根据虚拟机的运行状态,若是是否启用偏向锁(主要为了解决无锁的性能问题,如今锁基本都是可重入锁,在A线程得到锁后,会被标识为偏向锁,简单说就是有个标记,这个线程已经获取这个锁了,在锁的过程当中再竞争锁无需进行cas等操做,不会延迟本地调用,在释放偏向锁的会有必定的性能损耗,但对比偏向锁带来的提高,整体性能仍是有提高的)等,对象头会有不一样的设置方式。操作系统
调用init方法 : 在上面工做完成后,从虚拟机的角度新对象已经产生了,但从java程序角度来讲,对象建立才刚刚开始,在new指令后会接着执行<init>方法,把对象按照程序员的意愿进行初始化,这样一个对象就真正的产生了。线程
在HotSpot中,对象在内存中存储的布局可分为3块区域:对象头(Header)、实例数据(Instance Data)、对齐填充(Padding)。指针
偏向锁 : A线程得到锁,会在A线程的栈帧中变量和锁对象的对象头中存储该线程ID,当该线程再次尝试获取该对象锁时,不须要cas操做,只须要判断是不是当前线程,当栈帧出栈完成,A线程并不会释放锁,须要等到B线程竞争该锁才释放,(偏向锁的释放须要等到全局安全点,即在此时间点没有字节码执行),能够经过-XX:-UseBiasedLocking=false关闭偏向锁,则默认会进入轻量级锁。
轻量级锁 : A线程得到锁,会在a线程的栈帧里建立lock record(锁记录变量),让lock record的指针指向锁对象的对象头中的mark word,再让mark word 指向lock record,这就是获取了锁。B线程在锁竞争时,发现锁已经被A线程占用,则B线程不进入内核态,让B线程自旋,执行空循环,等待A线程释放锁。若是完成自旋策略仍是发现A线程没有释放锁,或者让C线程占用了,则B线程试图将轻量级锁升级为重量级锁。
重量级锁 : 让争抢锁的线程从用户态转换成内核态,使cpu借助操做系统进行线程协调。
实例数据:存储真正的有效信息,即代码中定义的字段内容,不管是从父类中继承下来的,仍是再子类中定义的,都须要记录下来。HotSpot虚拟机默认的分配策略为longs/doubles,ints,shorts/chars,bytes/booleans,oops,相同宽度的字段老是被分配到一块儿。再知足这个前提条件下,再父类中定义的变量会出如今子类以前。
对齐填充:不是必然存在的,也没有特别的含义,仅仅起着占位符的做用。因为HotSpot的自动内存管理系统要求对象起始地址必须是8字节的倍数,而对象头部分正好时8字节的倍数(1倍或2倍),所以,当对象实例数据部分没有对齐时,就须要经过对齐填充来补全。
创建对象即为了使用对象,咱们的Java程序须要根据栈上的reference数据来操做堆上的具体对象。因为reference类型在Java虚拟机规范中只规定了一个指向对象的引用,并无定义这个引用应该经过何种方式去定位、访问堆中的对象的具体位置,全部具体的访问方式取决于虚拟机实现,目前主流的访问方式有使用句柄和直接指针两种。
优缺点:使用句柄时,在对象被移动(垃圾收集时移动对象很广泛)时只会改变句柄中的实例数据指针,而reference自己不须要修改。只用直接指针速度更快,它节省了一次指针定位的时间开销(reference->对象相对于句柄的reference->句柄->对象),因为对象的访问很是频繁,所以这类开销聚沙成塔也是一项客观的执行成本成本。HotSpot使用直接指针来实现对象访问。
参考文献: 周志明.深刻理解Java虚拟机[M].第2版.北京:机械工业出版社