美团
对象在JVM
中是怎么存储的?
对象头信息里面有哪些东西?
蚂蚁金服
二面:java对象头
里有什么?
从字节码角度看待对象的创建过程
public class ObjectTest {
public static void main(String[] args) {
Object obj = new Object();
}
}
main( ) 方法对应的字节码(后面细讲)
0: new #2 // class java/lang/Object
3: dup
4: invokespecial #1 // Method java/lang/Object."<init>":()V
7: astore_1
8: return
定位到一个类的符号引用
,并且检查这个符号引用代表的类是否已经被加载,解析和初始化。(即判断类元信息是否存在)。双亲委派模式下,使用当前类加载器以ClassLoader + 包名 + 类名为key进行查找对应的.class文件
,如果没有找到文件,则抛出ClassNotFoundException异常,如果找到,则进行类加载,并生成对应的 Class 类对象。这个步骤有两个问题:
1.如何划分内存。
2.在并发情况下, 可能出现正在给对象A分配内存,指针还没来得及修改,对象B又同时使用了原来的指针来分配内存的情况。
划分内存的方法:(
默认用指针碰撞
)
如果内存规整:采用指针碰撞分配内存“指针碰撞”(Bump the Pointer)
标记压缩(整理)算法会整理内存碎片,堆内存一存对象,另一边为空闲区域
如果内存不规整: 采用“空闲列表”(Free List)
标记清除算法清理过后的堆内存,就会存在很多内存碎片
。处理并发安全问题
CAS(compare and swap)
本地线程分配缓冲(Thread Local Allocation Buffer,TLAB)
初始化零值之后,虚拟机要对对象进行必要的设置,例如这个对象是哪个类的实例、如何才能找到类的元数据信息、对象的哈希码、对象的GC分代年龄等信息。这些信息存放在对象的对象头Object Header之中。
在HotSpot虚拟机中,对象在内存中存储的布局可以分为3块区域:对象头(Header)、 实例数据(Instance Data)和对齐填充(Padding)。 HotSpot虚拟机的对象头包括两部分信息,第一部分用于存储对象自身的运行时数据, 如哈希码(HashCode)、GC分代年龄、锁状态标志、线程持有的锁、偏向线程ID、偏向时 间戳等。对象头的另外一部分是类型指针,即对象指向它的类元数据的指针,虚拟机通过这个指针来确定这个对象是哪个类的实例。
因此一般来说(由字节码中跟随invokespecial指令所决定),new指令之后会接着就是执行init方法,把对象按照程序员的意愿进行初始化,这样一个真正可用的对象才算完成创建出来。
从字节码角度看待init方法
/**
* 测试对象实例化的过程
* ① 加载类元信息 - ② 为对象分配内存 - ③ 处理并发问题 - ④ 属性的默认初始化(零值初始化)
* - ⑤ 设置对象头的信息 - ⑥ (属性的显式初始化/代码块中初始化 两者按照排序顺序执行,谁在前谁先执行)构造器中初始化
*
* 给对象的属性赋值的操作:
* ① 属性的默认初始化 - ② 显式初始化 / ③ 代码块中初始化 - ④ 构造器中初始化
*/
public class Customer{
int id = 1001;
String name;
Account acct;
{
name = "匿名客户";
}
public Customer(){
acct = new Account();
}
}
class Account{
}
对象头包含两部分:运行时元数据(Mark Word)和类型指针
运行时元数据
类型指针
指向类元数据InstanceKlass,确定该对象所属的类型。指向的其实是方法区中存放的类元信息
即是对象指向它的类元数据的指针,虚拟机通过这个指针来确定这个对象是哪个类的实例。
我们都知道getClass()方法是我们Java语言中获取对象类Class实例,并且可以通过Class获取这个类中的相关属性和方法,比如说:getClass().getName()可以得到该类的路径.. 这些功能都是靠底层c++/c语言中对象头中的类型指针实现的
getClass()返回运行时的类;
1.示例代码:
package com.dbzhang.demo4;
public class Person {
int id;
private String name;
public Person(int id, String name) {
super();
this.id = id;
this.name = name;
}}
package com.dbzhang.demo4;public class Test {
/**
- @param args
*/
public static void main(String[] args) {
Person p = new Person(1,"zdb");
System.out.println(p.getClass());
System.out.println(p.getClass().getName());
System.out.println(p.getClass().getSimpleName());
System.out.println(p.id);
}}
运行结果:class com.dbzhang.demo4.Person
com.dbzhang.demo4.Person
Person
1汇总:(1)getClass()方法是获得调用该方法的对象的类;getClass().getName()可以得到该类的路径;
(2)通过getClass()方法得到该对象类Class后,可以通过Class获取这个类中的相关属性和方法;
————————————————
版权声明:本文为CSDN博主「IT大兵」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/zdb292034/article/details/80725633
说明:如果对象是数组,还需要记录数组的长度
32位对象头
说明
它是对象真正存储的有效信息,包括程序代码中定义的各种类型的字段(包括从父类继承下来的和本身拥有的字段)
规则
相同宽度的字段总是被分配在一起
父类中定义的变量会出现在子类之前(父类在子类之前加载)
如果CompactFields参数为true(默认为true):子类的窄变量可以插入到父类变量的空隙
不是必须的,也没特别含义,仅仅起到占位符的作用
对象大小可以用jol-core包查看,引入依赖
<dependency>
<groupId>org.openjdk.jol</groupId>
<artifactId>jol-core</artifactId>
<version>0.9</version>
</dependency>
import org.openjdk.jol.info.ClassLayout;
/**
* 计算对象大小
*/
public class JOLSample {
public static void main(String[] args) {
ClassLayout layout = ClassLayout.parseInstance(new Object());
System.out.println(layout.toPrintable());
System.out.println();
ClassLayout layout1 = ClassLayout.parseInstance(new int[]{});
System.out.println(layout1.toPrintable());
System.out.println();
ClassLayout layout2 = ClassLayout.parseInstance(new A());
System.out.println(layout2.toPrintable());
}
// -XX:+UseCompressedOops 默认开启的压缩所有指针
// -XX:+UseCompressedClassPointers 默认开启的压缩对象头里的类型指针Klass Pointer,不压缩成员变量,只压缩类型指针
// Oops : Ordinary Object Pointers
public static class A {
//8B mark word
//4B Klass Pointer 如果关闭压缩-XX:-UseCompressedClassPointers或-XX:-UseCompressedOops,则占用8B
int id; //4B
String name; //4B 如果关闭压缩-XX:-UseCompressedOops,则占用8B
byte b; //1B
Object o; //4B 如果关闭压缩-XX:-UseCompressedOops,则占用8B
}
}
运行结果
java.lang.Object object internals:
OFFSET SIZE TYPE DESCRIPTION VALUE
0 4 (object header) 01 00 00 00 (00000001 00000000 00000000 00000000) (1) //mark word
4 4 (object header) 00 00 00 00 (00000000 00000000 00000000 00000000) (0) //mark word
8 4 (object header) e5 01 00 f8 (11100101 00000001 00000000 11111000) (-134217243) //Klass Pointer
12 4 (loss due to the next object alignment)
Instance size: 16 bytes
Space losses: 0 bytes internal + 4 bytes external = 4 bytes total
[I object internals:
OFFSET SIZE TYPE DESCRIPTION VALUE
0 4 (object header) 01 00 00 00 (00000001 00000000 00000000 00000000) (1)
4 4 (object header) 00 00 00 00 (00000000 00000000 00000000 00000000) (0)
8 4 (object header) 6d 01 00 f8 (01101101 00000001 00000000 11111000) (-134217363) //如果对象是数组,还需要记录数组的长度
12 4 (object header) 00 00 00 00 (00000000 00000000 00000000 00000000) (0)
16 0 int [I.<elements> N/A
Instance size: 16 bytes
Space losses: 0 bytes internal + 0 bytes external = 0 bytes total
com.tuling.jvm.JOLSample$A object internals:
OFFSET SIZE TYPE DESCRIPTION VALUE
0 4 (object header) 01 00 00 00 (00000001 00000000 00000000 00000000) (1)
4 4 (object header) 00 00 00 00 (00000000 00000000 00000000 00000000) (0)
8 4 (object header) 61 cc 00 f8 (01100001 11001100 00000000 11111000) (-134165407)
12 4 int A.id 0
16 1 byte A.b 0
17 3 (alignment/padding gap) // 和上面的byte A.b 的1字节构成4字节 进行对齐
20 4 java.lang.String A.name null
24 4 java.lang.Object A.o null
28 4 (loss due to the next object alignment)
Instance size: 32 bytes
Space losses: 3 bytes internal + 4 bytes external = 7 bytes total
1.jdk1.6 update14开始,在64bit操作系统中,JVM支持指针压缩
2.jvm配置参数:UseCompressedOops,compressed--压缩、oop(ordinary object pointer)--对象指针
3.启用指针压缩:-XX:+UseCompressedOops(默认开启),禁止指针压缩:-XX:-UseCompressedOops
1.在64位平台的HotSpot中使用32位指针(实际存储用64位),内存使用会多出1.5倍左右,使用较大指针在主内存和缓存之间移动数据,占用较大宽带,同时GC也会承受较大压力
2.为了减少64位平台下内存的消耗,启用指针压缩功能
3.在jvm中,32位地址最大支持4G内存(2的32次方),可以通过对对象指针的存入堆内存时压缩编码、取出到cpu寄存器后解码方式进行优化(对象指针在堆中是32位,在寄存器中是35位,2的35次方=32G),使得jvm只用32位地址就可以支持更大的内存配置(小于等于32G)
4.堆内存小于4G时,不需要启用指针压缩,jvm会直接去除高32位地址,即使用低虚拟地址空间
5.堆内存大于32G时,压缩指针会失效,会强制使用64位(即8字节)来对java对象寻址,这就会出现1的问题,所以堆内存不要大于32G为好,否则会占用大量的带宽,并且gc的压力也会变强
关于对齐填充:对于大部分处理器,对象以8字节整数倍来对齐填充都是最高效的存取方式。
JVM是如何通过栈帧中的对象引用访问到其内部的对象实例呢?
原文:https://www.cnblogs.com/tangliMeiMei/p/15228856.html