avatar

目录
浅析对象的创建过程

浅析对象的创建过程

前言

我们平时在创建对象时,可能都会这样创建:

java
1
Object object = new Object();

看起来很简单的一个过程,那么这个new操作的背后,有哪些相关的知识点,是需要我们掌握的,本文针对这些来展开介绍。

对象的创建过程

对象的创建过程

类都是由JVM加载到内存中的,类加载采用双亲委派机制,双亲委派机制具体信息,这里不作展开。类加载包含以下几个过程:

加载

加载Class信息到内存中,可以从Class文件读取,也可以从Zip包读取,或者在运行时通过动态代理读取。

连接

验证

验证Class文件的字节流中包含的信息是否符合虚拟机要求。

准备

为类对象的成员变量分配内存并设置类的成员变量的初始值

解析

虚拟机将常量池中的符号引用替换为直接引用

初始化

执行类构造器的init方法。

使用

对象的使用过程。

卸载

当类的所有对象都释放后执行卸载。

创建方式

对象的创建方式可以直接通过new的方式来创建,但一般为便于实现对象全局唯一,都会通过单例为实现。

单例的实现简单来分,有以下几种:

  • 饿汉式

  • 懒汉式

  • 静态内部类

  • 枚举

饿汉式

java
1
2
3
4
5
6
7
8
9
private static Singleton instance = new Singleton();

private Singleton() {

}

public Singleton getInstance() {
return instance;
}

饿汉式存在的问题是不能按需加载。

懒汉式

线程不安全

java
1
2
3
4
5
6
7
8
9
10
11
12
private static Singleton instance;

private Singleton() {

}

public Singleton getInstance() {
if (instance == null) {
instance = new Singleton();
}
return instance;
}

存在的问题:多线程的情况下,线程不安全。

线程安全

方法加锁
java
1
2
3
4
5
6
7
8
9
10
11
12
private static Singleton instance;

private Singleton() {

}

public synchronized Singleton getInstance() {
if (instance == null) {
instance = new Singleton();
}
return instance;
}

通过方法加锁,线程安全的问题是解决了,但是每次获取实例都会加锁,增加了开销。

代码加锁
直接加载
java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
private static Singleton instance;

private Singleton() {

}

public Singleton getInstance() {
if (instance == null) {
synchronized (Singleton.class) {
instance = new Singleton();
}
}
return instance;
}

锁不加在方法上,而是加在具体的代码块上。这种方式会存在创建两个对象的情况。

双重检查锁
  • 双重检查锁-不加Volatile
java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
private static Singleton instance;

private Singleton() {

}

public Singleton getInstance() {
if (instance == null) {
synchronized (Singleton.class) {
if (instance == null) {
instance = new Singleton();
}
}
}
return instance;
}

这种方式不会存在创建两个对象的情况。但会存在指令重排的问题。

  • 双重检查锁-加Volatile
Code
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
private static volatile Singleton instance;

private Singleton() {

}

public Singleton getInstance() {
if (instance == null) {
synchronized (Singleton.class) {
if (instance == null) {
instance = new Singleton();
}
}
}
return instance;
}

静态内部类

java
1
2
3
4
5
6
7
8
9
10
11
private Singleton() {

}

public Singleton getInstance() {
return SingletonHolder.INSTANCE;
}

static class SingletonHolder {
private static Singleton INSTANCE = new Singleton();
}

静态内部类的方式可以实现按需加载。

枚举

java
1
2
3
4
5
6
7
8
public enum Singleton {

INSTANCE;

public void doSomething() {

}
}

对象的内存结构

具体的可以参考上一篇文章。

对象头

Mark Word

包含HashCode,锁标识,对象年龄等信息。

Klass Pointer

对象的Class的内存地址

Length

这个只在数组类型的对象中存在,用来存放数组长度。

实例数据

存放对象的非静态成员变量信息。

对齐填充

JVM要求8字节对齐

对象分配

从GC的角度,可以将Java的堆分为新生代老年代。新生代和老年代的大小比为:1:2。

新生代

用来存放新产生的对象。新生代又分为Eden、SurvivorFrom、SurvivorTo三个区域,比例为:8:1:1。新生代使用的GC算法是复制算法。

Eden区

新对象产生后保存的地方。如果是较大的对象,则直接进入老年代。

SurvivorFrom区

GC后保留下来的对象会保存在这个区域。

SurvivorTo区

SurvivorFrom在GC后保留下来的对象会进入这个区域。

老年代

新生代中的对象每经过一次GC后,对应的年龄就会+1,如果对象的年龄达到老年代的标准(默认15次),则会进入老年代。老年代使用的GC算法是标记清除算法。

文章作者: milovetingting
文章链接: http://www.milovetingting.cn/2020/05/21/Java/%E6%B5%85%E6%9E%90%E5%AF%B9%E8%B1%A1%E7%9A%84%E5%88%9B%E5%BB%BA%E8%BF%87%E7%A8%8B/
版权声明: 本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 milovetingting

评论