04-Obj
1. 对象创建过程
- class_loading
- class_linking(verification, preparation 类变量赋默认值, resolution)
- class_initializing(类变量赋初始值,执行
static{}
) - 申请对象内存
- 成员变量赋默认值
- 调用构造方法
<init>
- 成员变量顺序赋初始值
- 执行构造方法语句
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. 普通对象
- 对象头:markword(8B)
- ClassPointer指针(Class对象指针)
-XX:+UseCompressedClassPointers
为4B,不开启为8B
- 实例数据instance(成员变量)
- 引用类型:
-XX:+UseCompressedOops
为4B,不开启为8B - Oops = Ordinary_Object_Pointers
- 引用类型:
- Padding对齐,8的倍数
2.3. 数组对象
- 对象头:markword(8B)
- ClassPointer指针(Class对象指针)
- 数组长度:4B
- 数组数据
- Padding对齐,8的倍数
3. 对象大小验证
原理:class在load内存过程中,agent代理拦截instrument,获取其大小
1. ObjectSizeAgent.jar
Java中对bytecode的处理和调试统称为instrument
- 新建项目ObjectSizeAgent(1.8)
- 创建
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);
}
}
pom.xml
<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>
- 其他项目引入Agent_Jar包
- idea运行加
Add VM options
# -:代表关闭ClassPointer压缩
# +:代表开启ClassPointer压缩
-javaagent:/Users/listao/mca/mca_proj/jvm/src/main/java/lib/ObjectSizeAgent-1.0-SNAPSHOT.jar
-XX:-UseCompressedClassPointers
- 测试对象大小
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位机)
- 内存并不是越大越好
- 4G以下,直接砍掉高32位
- 4G ~ 32G,默认开启内存压缩ClassPointers_Oops
- 32G,压缩无效,使用64位
4. 对象头具体内容
Hotspot源码,C++写的
- markword根据对象状态而不同,至少了解:
- 锁的标志位。3b
- GC标记,分代年龄。GC年龄为15,因为分代年龄为4b
- 对象的hashCode,在调用的时候才会被写到markword中。没重写过,为identityHashCode,重写了就存到别的地方了
- (32位)
- hashCode部分:
- 31位hashcode =>
System.identityHashCode(…)
- 按原始内容计算的hashcode,重写过的
hashcode()
计算的结果不会存在这里
- 31位hashcode =>
- 如果对象没有重写
hashcode()
,那么默认是调用os::random
产生hashcode,可以通过System.identityHashCode
获取;os::random
产生hashcode的规则为next_rand = (16807 seed)mod(2^31 - 1)
,因此可以使用31位存储;另外一旦生成了hashcode,JVM会将其记录在markword中 - 什么时候会产生hashcode?
- 调用未重写的
hashcode()
方法以及System.identityHashCode
的时候
- 调用未重写的
- 为什么GC年龄默认为15?(最大为15)
- 死磕Synchronized底层实现,面试你还怕什么?
- 面试题深入解析:Synchronized底层实现
- 死磕Synchronized底层实现--重量级锁
- 一文让你读懂Synchronized底层实现,秒杀面试官
5. 对象定位
- 访问对象两种方式--句柄和直接指针
T t = new T()
t是怎样找到内存中的对象的
1. 直接指针
- Hotspot默认
2. 句柄池
- GC时效率高