1.检查常量池
虚拟机遇到一条new指令时,首先将去检查这个指令的参数是否能否在常量池中定位到一个符号引用,并且检查这个符号代表的类是否被加载、解析和初始化过。如果没有则执行类加载。
2.分配内存
对象所需内存的大小在类加载完成后便可以完全确定,为对象分配空间的任务等同与把一块确定大小的内存从堆中划分出来。根据内存是否绝对规整,分为“指针碰撞”和“空闲列表”两种。选择哪种由Java堆来决定。
内存分配完成后,虚拟机需要将分配到的内存空间初始化为零值(不包含对象头);
3.设置对象信息
对对象进行必要的设置,这些信息放在对象的对象头中。
4.创建对象
完成上面的工作后,从虚拟机角度来看,已经有对象,但是从java程序角度,对象才刚刚开始创建。——<init>方法还没有执行,所有字段均为0,所以执行之后new之后将执行<init>方法,把对象按照程序猿的意愿初始化,产生一个有用对象。这个<inti>即对象级变量初始化。
5.对象布局
对象头
存储对象自身的运行时数据:Mark Word(在32bit和64bit虚拟机上长度分别为32bit和64bit),包含如下信息:
- 对象hashCode
- 对象GC分代年龄
- 锁状态标志(轻量级锁、重量级锁)
- 线程持有的锁(轻量级锁、重量级锁)
- 偏向锁相关:偏向锁、自旋锁、轻量级锁以及其他的一些锁优化策略是+ JDK1.6加入的,这些优化使得Synchronized的性能与ReentrantLock的性能持平,在Synchronized可以满足要求的情况下,优先使用Synchronized,除非是使用一些ReentrantLock独有的功能,例如指定时间等待等。
** 类型指针**:对象指向类元数据的指针(32bit-->32bit,64bit-->64bit(未开启压缩指针),32bit(开启压缩指针))
- JVM通过这个指针来确定这个对象是哪个类的实例(根据对象确定其Class的指针)
实例数据:对象真正存储的有效信息
对齐填充
JVM要求对象的大小必须是8的整数倍,若不是,需要补位对齐
总结:一共有Header、Instant Data、Padding三部分。
6.详解内部对象
- 使用匿名内部类时,我们必须是继承一个类或者实现一个接口,但是两者不可兼得,同时也只能继承一个类或者实现一个接口。
- 匿名内部类中是不能定义构造函数的。
- 匿名内部类中不能存在任何的静态成员变量和静态方法。
- 匿名内部类为局部内部类,所以局部内部类的所有限制同样对匿名内部类生效。
- 匿名内部类不能是抽象的,它必须要实现继承的类或者实现的接口的所有抽象方法。
给匿名内部类传递参数的时候,若该形参在内部类中需要被使用,那么该形参必须要为final。也就是说:当所在的方法的形参需要被内部类里面使用时,该形参必须为final。
为什么必须要为final呢?
见http://www.jianshu.com/writer#/notebooks/3857227/notes/9622871/preview。