JVM对象实例化
- 简介/背景
- 一、创建对象的方式
- 1. new
- 2. Class对象的newInstance方法
- 3. Construstor对象的newInstance(xx)方法
- 4. 使用clone方法
- 二、创建对象的步骤
- 1. 判断对象是否已经加载、链接、初始化
- 2. 为对象分配内存
- 3. 处理并发安全问题
- 4. 初始化分配到的空间
- 5. 设置对象的对象头
- 6. 执行init方法进行初始化
- 总结
简介/背景
本篇文章为博主学习JVM教程时,记录的笔记,主要介绍了一下Java对象创建的几种方式和创建对象的步骤。
tips: 思维导图
一、创建对象的方式
创建对象课程中提到了六种方式,这里针对前面四种方式进行举例介绍。
1. new
在java中使用new关键字进行创建对象,有以下三种形式。
形式一
使用最普通的new的形式,举例如下:
public class Test {
public static void main(String[] args) {
Object o = new Object();
}
}
形式二
通过静态方法创建对象
public class Test {
public static void main(String[] args) {
Student student = Student.newInstance();
}
}
class Student {
public Integer id;
public static Student newInstance() {
return new Student();
}
public Integer getId() {
return id;
}
public Student setId(Integer id) {
this.id = id;
return this;
}
}
形式三
通过build()方法构建对象
public class Test {
public static void main(String[] args) {
Student student = new Student.Builder().build();
}
}
class Student {
static class Builder {
public Student build() {
return new Student();
}
}
}
2. Class对象的newInstance方法
通过获取类的Class对象,来创建对象实例。
public class Test {
public static void main(String[] args) {
try {
Student student = Student.class.newInstance();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
}
class Student {
}
3. Construstor对象的newInstance(xx)方法
因为Class对象的newInstance方法只能使用默认的构造方法产生对象,所以java已经不建议使用了,建议用Contrustor对象的newInstance方法,有参或者无参的构造方法都可以使用。
public class Test {
public static void main(String[] args) {
try {
Constructor<Student> constructor = Student.class.getConstructor(Integer.class);
Student student = constructor.newInstance(123);
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
}
class Student {
public Integer id;
public Student(Integer id) {
this.id = id;
}
}
4. 使用clone方法
通过实现Cloneable接口,重写clone方法来产生类。
public class Test {
public static void main(String[] args) {
Student student = new Student();
Student newStudent = student.clone();
}
}
class Student implements Cloneable {
public Student() {}
@Override
public Student clone() {
try {
Student clone = (Student) super.clone();
// TODO: copy mutable state here,
// so the clone can't change the internals of the original
return clone;
} catch (CloneNotSupportedException e) {
throw new AssertionError();
}
}
}
二、创建对象的步骤
1. 判断对象是否已经加载、链接、初始化
JVM的类加载机制,是按需加载的方式。就是我们需要使用该类的时候,才会将生成的class文件加载到内存当中生成class对象。
如果要创建一个对象,首先要判断相应的class文件是否已经生成了class对象,如果虚拟机还没有生成Class对象,类加载子系统就会去加载。
2. 为对象分配内存
首先计算对象占用空间的大小,接着在堆中划分一块内存给到新对象,如果实例成员变量是引用变量,仅分配引用变量空间即可,即4个字节大小。这里分为两种情况,如果内存是规整的,采用的是指针碰撞的方法。如果内存不是规整的采用的空闲列表分配的方法。
指针碰撞法
如果内存是规整的,那么虚拟机将采用指针碰撞法(Bumo The Pointer)来为对象分配内存。
意思是所有用过的内存在一边,空闲的内存在另一边,中间放着一个指针作为分界点的指示器,分配内存就仅仅是把指针向空闲那边挪动一段与对象大小相等的距离罢了。如果垃圾收集器选择的是Serial、ParNew这种基于压缩算法的,虚拟机采用这种分配方式。一般使用带有Compact(整理)过程的收集器时,使用指针碰撞。
空闲列表法
应用场合:堆内存不规整(虚拟机维护一个可以记录内存块是否可以用的列表来了解内存分配情况)。
即在开辟内存空间时候,找到一块足够大的内存块分配给该对象即可,同时更新记录列表。
将堆内存这样划分的代表的GC收集器算法有:CMS
3. 处理并发安全问题
这一步,有的文章会合并在上一步里来说,这里把它拆出来说明一下。
如果是在多线程的情况下,分配内存就会存在并发安全的问题,那么JVM是如何处理的呢?
多线程分配内存时,虚拟机为每个线程分配了不同的空间,这样每个线程在分配内存时只是在自己的空间中操作,从而避免了上述问题,不需要同步。当然,当线程自己的空间用完了才需要需申请空间,这时候需要进行同步锁定。为每个线程分配的空间称为“本地线程分配缓冲(TLAB)”,是否启用TLAB需要通过 -XX:+/-UseTLAB参数来设定。
4. 初始化分配到的空间
虚拟机需要将分配到的内存空间都进行初始化(即给一些默认值),这将做是为了保证对象实例的字段在Java代码中可以在不赋初值的情况下使用。程序可以访问到这些字段对用数据类型的默认值。
5. 设置对象的对象头
虚拟机对对象进行一些简单设置,如标记该对象是哪个类的实例,这个对象的hash码,该对象所处的年龄段等等(这些可以理解为对象实例的基本信息)。这些信息被写在对象头中。jvm根据当前的运行状态,会给出不同的设置方式。
6. 执行init方法进行初始化
最后执行由开发人员编写的对象的初始化方法,把对象按照开发人员的设计进行初始化,一个对象便创建出来了。
总结
本篇文章大概介绍了JVM创建对象的几种方式,以及创建对象的步骤主要有:
1. 判断对象是否已经加载、链接、初始化
2. 为对象分配内存
3. 处理并发安全问题
4. 初始化分配到的内存空间
5. 设置对象头
6. 执行init方法进行初始化。
❤️ 博主笔力尚浅,文中有疏漏之处还请流言指正,不胜感激。❤️ 谢谢大家。❤️