这篇博客是为了深入探究 Java 中对象的知识。
首先我们先看下一个简单创建对象的代码,看一个对象到底是如何在内存中创建的。
public static void main(String[] args) {
Person obj = new Person();
}
public class Person {
private int age=8;
private String name="fu";
}
对应的 JVM 指令:
0 new #2 <learnJava/AQS/Person>
3 dup
4 invokespecial #3 <learnJava/AQS/Person.<init>>
7 astore_1
8 return
对应指令含义:
new
: 创建一个实例对象。
dup
: 复制栈顶数值并将复制值压入栈顶
invokespecial
:调用超类构建方法, 实例初始化方法, 私有方法
astore_1
:将栈顶引用类型数值存入指定本地变量
具体的初始化过程如下:
在常用的单例模式中,有一个 double check 模式 ,具体代码如下:
private volatile static Singleton05 singleton05;
//double check 性能最好
public static Singleton05 getInstance() {
if (singleton05 == null) {
synchronized (Singleton05.class) {
if (singleton05 == null) {
singleton05 = new Singleton05();
}
}
}
return singleton05;
}
在诸多的单例模式中, double check lock 是性能和简单的方式之一,在上面对象的定义中,使用了 volatitle 的关键字描述,如果此时我们没有使用 volatitlte 关键字会怎样?
因为在初始化的时候,存在一个半初始化的状态,其实是已经创建的对象,但是对象中的字段为 0,此时 DCL 检查不为空的时候,则满足了条件,即会直接返回,导致意想不到的结果。
Volatitle 关键字有两个作用
Java 对象一般分为 3 块空间:对象头
,实例数据
和对齐空间
。在数组对象中又单独增加的数据长度的空间,具体几个对象布局如下:
问题: 一个 Object 占用几个字节?
答案:16 字节,算法分为两种:
如果是 Object obj=new Object(); 则 obj 占用了 4 个字节,所以一共占用了 20 字节。
在对象中一般会保留类的引用,称为类指针(Class Pointer)。同样,指向实例也会有一个指针,称为对象指针(OOP)。这个指针一般是 8 个字节,压缩后变成 4 个字节。
为什么要存在指针压缩呢?目的肯定是为了节省内存,为什么压缩指针能行的通呢?我个人觉得有两个原因:
对象都是 8 字节对齐的,所以指针后都是相对的 “整数“(能整除 8)
例如: (不是 JVM 的指针压缩,只是表达指针压缩的含义)
16 00000000 00001000
32 00000000 00010000
64 00000000 00100000
可以转为:
//舍弃 3 个 0
16 00000000 00001 000
32 00000000 00010 000
64 00000000 00100 000
在 JVM 中有两个参数来控制指针
UseCompressClassPointers : 类指针压缩
UseCompressOop : 对象指针压缩