JVM虚拟机-对象的创建过程
目录
本文是针对学习《深入理解java虚拟机》2.3章,对象创建部分的学习笔记
1.创建对象的方式
package com.zhe.yang.java.jvm.createObject;
import java.io.*;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
public class Demo implements Cloneable, Serializable {
private int id;
/*get set 方法省略*/
public Demo() {
}
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
@Override
public String toString() {
return "Demo{" +
"id=" + id +
'}';
}
public static void main(String[] args) throws IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException, CloneNotSupportedException, IOException {
/*使用new 关键字创建对象*/
Demo demo = new Demo(123);
//System.out.println("使用反射通过无参构造函数创建对象:"+demo.toString());
/*使用反射通过无参构造函数创建对象*/
Demo demo1 = Demo.class.newInstance();
//System.out.println("使用反射通过无参构造函数创建对象:"+demo1.toString());
/*使用反射通过有参构造函数创建对象*/
Constructor<Demo> constructor = Demo.class.getConstructor(int.class);
Demo demo2 = constructor.newInstance(234);
//System.out.println("使用反射通过有参构造函数创建对象:"+demo2.toString());
/*通过克隆对象*/
Demo clone = (Demo)demo1.clone();
//System.out.println("通过克隆对象:"+clone.toString());
/*通过序列化与反序列化创建对象*/
ObjectOutputStream outputStream=new ObjectOutputStream(new FileOutputStream("demo_serializable"));
outputStream.writeObject(demo);
outputStream.close();
ObjectInputStream objectInputStream=new ObjectInputStream(new FileInputStream("demo_serializable"));
try {
Demo demo_serializable = (Demo)objectInputStream.readObject();
//System.out.println("通过序列化与反序列化创建对象:"+demo_serializable.toString());
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
}
2.new对象的创建过程
2.1对象创建检查:
虚拟机在执行new指令时,首先检查这个指令参数能否在常量池中确定到一个类的符号引用,并且检查这个符号引用的类是否被加载,解析和初始化过。如果没有,必须先执行相应的类加载过程。
2.2虚拟机为对象分配内存
在类加载检查通过后,虚拟机将为新生对象分配内存。对象所需内存的大小在类加载完成后就可以完全确定,为对象分配内存的任务等同于把一块确定大小的内存从堆中划分出来。
内存分配引出的问题:内存分配方式与线程安全问题。
堆内存是否规整由所采用的垃圾收集器是否带有压缩整理决定
2.2.1.内存分配方式1-指针碰撞:
假设堆中内存是绝对规整,已用过的内存放在一边,空闲的内存放在一边,中间放着一个指针作为分界点的指示器,那所分配内存就是将指针向空闲内存空间挪动一段与对象大小相等的距离,这种方式为“指针碰撞”。
在使用Serial,ParNew等带有Compact过程的收集器时,采用此重方式。
2.2.2.内存分配方式2-空闲列表:
如果堆中内存不是规整的,已使用的内存和空闲内存相互交错,虚拟机必须维护一个列表,记录哪些内存块可用,在分配时从列表中找到足够大的空间划分给对象实例,并更新列表上的记录,这种方式为“空闲列表”
在使用CMS这种基于mark-sweep算法收集器时,采用空闲列表。
2.2.3.为对象分配内存时的线程安全问题:
现象:正在给对象A分配内存,指针还没来得及修改,对象B又同时使用原来的指针分配内存的情况
解决方法1:对分配内存空间的动作进行同步处理-实际上虚拟机采用CAS配上失败重试的方式保证更新操作的原子性
解决方法2:把内存分配的动作按照线程划分在不同的空间中进行,即每个线程在堆中预先分配一小块内存,称为“本地线程分配缓冲”(TLAB)。哪个线程要分配内存,就在哪个线程的TLAB上分配,只有TLAB用完并分配新的TLAB时,才需要同步锁定。虚拟机是否使用TLAB,可通过-XX:+/-UseTLAB参数来设定。TLAB是在堆上分配给线程的私有内存。
2.3 对象初始化
a. 内存分配后,虚拟机需要将分配的内存空间都初始化为零值(不包括对象头),如果使用TLAB,这一过程提前到TLAB分配时进行。此操作保证对象的实例字段在java代码中可以不赋初始值就可直接使用。
b. 设置对象头的相关信息,对象头中包含类实例,元数据信息,hash,GC分代年龄等,以及锁信息等。
c. 上面步骤执行后虚拟机层面的创建对象完成,从java程序的视角看,对象创建才刚开始<init>方法还没执行,所有字段都为0.执行new指令后接着执行<init>方法,按照代码进行初始化。
d.初始化完成后,对象创建完毕。
说明:在对象创建时,new关键字使用<init>方法,接<invokespecial>完成,而其他方式创建对象,使用<invokevirtual>方式创建。具体解释说明,以及对象的初始化,类加载等会在以后进行学习讲解。
附对象创建方式的main方法的字节码,用于观察创建命令:
public static void main(java.lang.String[]) throws java.lang.IllegalAccessException, java.lang.InstantiationException, java.lang.NoSuchMethodException, java.lang.reflect.InvocationTargetException, java.lang.CloneNotSupportedException, java.io.IOException;
descriptor: ([Ljava/lang/String;)V
flags: ACC_PUBLIC, ACC_STATIC
Code:
stack=5, locals=9, args_size=1
0: new #11 // class com/zhe/yang/java/jvm/createObject/Demo
3: dup
4: bipush 123
6: invokespecial #12 // Method "<init>":(I)V
9: astore_1
10: ldc #11 // class com/zhe/yang/java/jvm/createObject/Demo
12: invokevirtual #13 // Method java/lang/Class.newInstance:()Ljava/lang/Object;
15: checkcast #11 // class com/zhe/yang/java/jvm/createObject/Demo
18: astore_2
19: ldc #11 // class com/zhe/yang/java/jvm/createObject/Demo
21: iconst_1
22: anewarray #14 // class java/lang/Class
25: dup
26: iconst_0
27: getstatic #15 // Field java/lang/Integer.TYPE:Ljava/lang/Class;
30: aastore
31: invokevirtual #16 // Method java/lang/Class.getConstructor:([Ljava/lang/Class;)Ljava/lang/reflect/Constructor;
34: astore_3
35: aload_3
36: iconst_1
37: anewarray #17 // class java/lang/Object
40: dup
41: iconst_0
42: sipush 234
45: invokestatic #18 // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
48: aastore
49: invokevirtual #19 // Method java/lang/reflect/Constructor.newInstance:([Ljava/lang/Object;)Ljava/lang/Object;
52: checkcast #11 // class com/zhe/yang/java/jvm/createObject/Demo
55: astore 4
57: aload_2
58: invokevirtual #20 // Method clone:()Ljava/lang/Object;
61: checkcast #11 // class com/zhe/yang/java/jvm/createObject/Demo
64: astore 5
66: new #21 // class java/io/ObjectOutputStream
69: dup
70: new #22 // class java/io/FileOutputStream
73: dup
74: ldc #23 // String demo_serializable
76: invokespecial #24 // Method java/io/FileOutputStream."<init>":(Ljava/lang/String;)V
79: invokespecial #25 // Method java/io/ObjectOutputStream."<init>":(Ljava/io/OutputStream;)V
82: astore 6
84: aload 6
86: aload_1
87: invokevirtual #26 // Method java/io/ObjectOutputStream.writeObject:(Ljava/lang/Object;)V
90: aload 6
92: invokevirtual #27 // Method java/io/ObjectOutputStream.close:()V
95: new #28 // class java/io/ObjectInputStream
98: dup
99: new #29 // class java/io/FileInputStream
102: dup
103: ldc #23 // String demo_serializable
105: invokespecial #30 // Method java/io/FileInputStream."<init>":(Ljava/lang/String;)V
108: invokespecial #31 // Method java/io/ObjectInputStream."<init>":(Ljava/io/InputStream;)V
111: astore 7
113: aload 7
115: invokevirtual #32 // Method java/io/ObjectInputStream.readObject:()Ljava/lang/Object;
118: checkcast #11 // class com/zhe/yang/java/jvm/createObject/Demo
121: astore 8
123: goto 133
126: astore 8
128: aload 8
130: invokevirtual #34 // Method java/lang/ClassNotFoundException.printStackTrace:()V
133: return
参考资料:
1.《深入理解java虚拟机》2.3章节
2. 参考博客《https://blog.csdn.net/justloveyou_/article/details/72466416》