java对象的创建过程
- jvm遇到new指令,回去检查这个指令的参数是否能在常量池中定位到一个类的符号引用,并且在检查这个符号引用代表的类是否已经被加载、解析和初始化过。如果没有就执行类加载过程。
- 为对象分配空间,把一块固定大小的内存从java堆里划分出来。如果java堆时那种整齐的,一边放已经用过的内存,一边放没有用过的内存,这样就有一个中间指针隔开两个区域,只要把这个指针移到相应位置即可,这种方法叫指针碰撞。如果已经使用内存和未使用的内存交错,就需要一个空闲列表方法来决定分配在哪里。
- 多线程并发时会出现正在给对象A分配内存,还没来得急修改指针,对象B又用这个指针分配内存,这就出现问题了,两种解决方法:
- 对分配的内存空间的动作进行同步
- 把内存分配动作按照线程划分在不同的空间之中进行。
- 内存分配完之后jvm将分配到的内存全都初始化为零。接下来设置对象头,包括这个对象是哪个类的实例,如何才能找到该类的原数据信息,对象的哈希码,对象的GC分代年龄等信息。
- 执行init,初始化。
对象的内存布局
- 对象头:一部分是存储对象自身的运行时数据,如哈希码,GC分代年龄等。另一部分时类型指针,即对象指向类原数据的指针。
- 实例数据
- 对齐填充
五种情况必须初始化
- 遇到new,getstatic, putstatic,invokestatic这4条字节码的时候,如果类没有进行初始化,则需要先触发其初始化。
- 使用java.lang.reflect包方法的对类进行反射调用的时候
- 初始化一个类,他的父类还没初始化,则先触发父类的初始化
- 当虚拟机启动时,用户需要指定一个执行主类,虚拟机会先初始化这个类
- 当使用JDK1.7时动态语言支持的时候,一个java.lang.invoke.MethodHandle实例最后解析结果为REF_getstatic,REF_pustatic,REF_invokeStatic的方法句柄,并且这个方法句柄所对应的类没有进行初始化,则需要先触发其初始化。
加载阶段,jvm需要完成以下
- 通过一个类的全限定名来获取定义此类的二进制字节流。
- 将定义类的二进制字节流所代表的静态存储结构转换为方法区的运行时数据结构
- 在java堆中生成一个代表该类的java.lang.Class对象,作为方法区数据的访问入口。