04-Obj

1. 对象创建过程

  1. class_loading
  2. class_linking(verification, preparation 类变量赋默认值, resolution)
  3. class_initializing(类变量赋初始值,执行static{}
  4. 申请对象内存
  5. 成员变量赋默认值
  6. 调用构造方法<init>
    1. 成员变量顺序赋初始值
    2. 执行构造方法语句

2. 对象内存布局

  • 和VM实现和设置都有关系
  • 对象大小(64位机)

1. 查看VM配置

➜  ~# java -XX:+PrintCommandLineFlags -version
-XX:InitialHeapSize=536870912 -XX:MaxHeapSize=8589934592 -XX:+PrintCommandLineFlags -XX:+UseCompressedClassPointers -XX:+UseCompressedOops -XX:+UseParallelGC
java version "1.8.0_212"
Java(TM) SE Runtime Environment (build 1.8.0_212-b10)
Java HotSpot(TM) 64-Bit Server VM (build 25.212-b10, mixed mode)

2. 普通对象

  1. 对象头:markword(8B)
  2. ClassPointer指针(Class对象指针)
    • -XX:+UseCompressedClassPointers为4B,不开启为8B
  3. 实例数据instance(成员变量)
    • 引用类型:-XX:+UseCompressedOops为4B,不开启为8B
    • Oops = Ordinary_Object_Pointers
  4. Padding对齐,8的倍数

2.3. 数组对象

  1. 对象头:markword(8B)
  2. ClassPointer指针(Class对象指针)
  3. 数组长度:4B
  4. 数组数据
  5. Padding对齐,8的倍数

3. 对象大小验证

原理:class在load内存过程中,agent代理拦截instrument,获取其大小

1. ObjectSizeAgent.jar

Java中对bytecode的处理和调试统称为instrument

  1. 新建项目ObjectSizeAgent(1.8)
  2. 创建ObjectSizeAgent
public class ObjectSizeAgent {
    private static Instrumentation inst;

    /**
     * 方法premain固定
     * jvm自动传_inst
     */
    public static void premain(String agentArgs, Instrumentation _inst) {
        inst = _inst;
    }

    public static long sizeOf(Object o) {
        return inst.getObjectSize(o);
    }
}
  1. pom.xml

javaagent使用指南open in new window

<build>
    <plugins>
        <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-jar-plugin</artifactId>
            <version>3.1.0</version>
            <configuration>
                <archive>
                    <!-- 自动添加META-INF/MANIFEST.MF -->
                    <manifest>
                        <addClasspath>true</addClasspath>
                    </manifest>
                    <manifestEntries>
                        <!-- 在主jar运行前运行指定的jar -->
                        <Premain-Class>com.listao.jvm.ObjectSizeAgent</Premain-Class>
                        <Can-Redefine-Classes>true</Can-Redefine-Classes>
                        <Can-Retransform-Classes>true</Can-Retransform-Classes>
                    </manifestEntries>
                </archive>
            </configuration>
        </plugin>
    </plugins>
</build>
  1. 其他项目引入Agent_Jar包
image-20230412112111281
  1. idea运行加Add VM options
# -:代表关闭ClassPointer压缩
# +:代表开启ClassPointer压缩
-javaagent:/Users/listao/mca/mca_proj/jvm/src/main/java/lib/ObjectSizeAgent-1.0-SNAPSHOT.jar
-XX:-UseCompressedClassPointers
  1. 测试对象大小
import com.listao.jvm.ObjectSizeAgent;

public class T7_SizeOfAnObject {
    public static void main(String[] args) {
        System.out.println(ObjectSizeAgent.sizeOf(new Object()));
        System.out.println(ObjectSizeAgent.sizeOf(new int[]{}));
        System.out.println(ObjectSizeAgent.sizeOf(new P()));
    }

    /**
     * 一个Object占多少个字节
     * 1. -XX:+UseCompressedClassPointers => Class的指针是否压缩:1-压缩4B, 2-未压缩8B
     * 2. -XX:+UseCompressedOops => 普通对象的指针是否压缩:1-压缩4B, 2-不压缩8B
     * 3. Oops = ordinary_object_pointers => 普通对象指针
     */
    private static class P {
        // markword:8B
        // ClassPointers:4B 8B

        int id;         // 4
        // String name;    // 4
        // int age;        // 4

        // byte b1;        // 1
        // byte b2;        // 1

        // Object o;       // 4
    }
}
---------------------------------------------
16
24
24

2. Hotspot内存压缩规则

  • (64位机)
  • 内存并不是越大越好
  1. 4G以下,直接砍掉高32位
  2. 4G ~ 32G,默认开启内存压缩ClassPointers_Oops
  3. 32G,压缩无效,使用64位

4. 对象头具体内容

Hotspot源码,C++写的

image-20220712090514484
  1. markword根据对象状态而不同,至少了解:
    1. 锁的标志位。3b
    2. GC标记,分代年龄。GC年龄为15,因为分代年龄为4b
    3. 对象的hashCode,在调用的时候才会被写到markword中。没重写过,为identityHashCode,重写了就存到别的地方了
  2. (32位)
image-20230422134514228
  1. hashCode部分:
    • 31位hashcode => System.identityHashCode(…)
    • 按原始内容计算的hashcode,重写过的hashcode()计算的结果不会存在这里
  2. 如果对象没有重写hashcode(),那么默认是调用os::random产生hashcode,可以通过System.identityHashCode获取;os::random产生hashcode的规则为next_rand = (16807 seed)mod(2^31 - 1),因此可以使用31位存储;另外一旦生成了hashcode,JVM会将其记录在markword中
  3. 什么时候会产生hashcode?
    • 调用未重写的hashcode()方法以及System.identityHashCode的时候
  4. 为什么GC年龄默认为15?(最大为15)

5. 对象定位

1. 直接指针

  • Hotspot默认
image-20230413095428627

2. 句柄池

  • GC时效率高
image-20230414195855543