一、一线互联网面试题

     首先先看下下面的面试题:

java中的对象头 jvm对象头_对象头

二、对象的创建过程

首先在创建对应的时候如果对象不存在肯定是涉及到class文件的加载,class文件的加载需要经历:加载 ->连接->初始化。

  1. class loading:加载
  2. class linking:连接,连接又有具体的三步,分别是:验证(验证包括:calss文件格式验证、meta元数据验证等)、准备、解析:对应会给变量赋默认值。
  3. class initializing 静态初始化的过程,这里会给变量赋值初始值;
  4. 申请对象内存
  5. 成员变量赋默认值;
  6. 调用构造方法<init>,这里会给成员变量顺序赋初始值,执行构造方法语句:首先去调用super。

三、对象在内存中的存储布局

2.1观察虚拟机配置的命令:

java -XX:+PrintCommandLineFlags -version

        这个就是我本机默认的虚拟机配置参数:

java中的对象头 jvm对象头_对象头_02

 2.2普通对象

        内存里的一个Java对象分为三部分:对象头,实例数据,对齐、class Pointer 类指针

  1. 对象头:markword  8Bytes 用于标记锁信息、GC信息、IdentityHashCode等。
  2. class Pointer 类指针:jvm开启内存压缩(-XX:+UseCompressedClassPointer),4字节。不开启,8字节。默认开启,用于标记该对象是哪个Class的实例 例如:Object.class。
  3. 实例数据(instance data):包括对象的所有成员变量,大小由各个成员变量决定,如果没有成员变量,则这一块为空,基本类型 (int,float 4字节 long,double 8字节,char,short 2字节,byte 1字节 ,boolean 实际大小1bit  占用1字节)。引用类型:-XX:+UseCompressedOops 为4字节 不开启为8字节,Oops Ordinary Object Pointers;
  4. Padding对齐:8的倍数(因为读取的时候不是按直接读的,是读的块,一般是8个字节的倍数,这样效率会更高);

2.3数组对象

        数组对象跟普通对象是差不多的,只不过比普通对象多出了一块数据:数组长度;

        数组长度:4字节 标记数组有多少个元素;

        下面做一个实验,做这个实验,首先我们需要自己做一个工具jar,实验:利用java agent(class文件到内存之间的代理,这个代理得自己去实现)的机制。下面补充了种用JOL工具的,比较简单。

1、创建文件ObjectSizeAgent(类似到内存前有个代理)

public class ObjectSizeAgent {
    // 类似调弦的作用
    private static Instrumentation inst;
 
    public static void premain(String agentArgs, Instrumentation _inst) {
        inst = _inst;
    }
 
    public static long sizeOf(Object o) {
        return inst.getObjectSize(o);
    }
}

        2、src目录下创建META-INF/MANIFEST.MF

        这里可以只要Premain-Class: com.mashibing.jvm.agent.ObjectSizeAgent;

Manifest-Version: 1.0
Created-By: mashibing.com
Premain-Class: com.mashibing.jvm.agent.ObjectSizeAgent

        3、打包jar文件     

        4、在需要使用该Agent Jar的项目中引入该Jar包 project structure - project settings - library 添加该jar包

        5、运行时需要该Agent Jar的类,加入参数:

        执行用哪个jar文件当作代理来运行我的虚拟机:

-javaagent:C:\work\ijprojects\ObjectSize\out\artifacts\ObjectSize_jar\ObjectSize.jar

        6、如何使用该类:

package com.mashibing.jvm.c3_jmm;
      
      import com.mashibing.jvm.agent.ObjectSizeAgent;
      
      public class T03_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()));
          }
      
          private static class P {
                              //8 _markword
                              //4 _oop指针
              int id;         //4
              String name;    //4
              int age;        //4
      
              byte b1;        //1
              byte b2;        //1
      
              Object o;       //4
              byte b3;        //1
      
          }
      }

        测出来 object:16个字节 object大小分析:对象头是8个字节,本来应该是8个字节(64位的)因为默认打开时压缩的,所以加上被压缩成4个字节(class_pointer ),一共是12字节,再加上有4字节的填充数据,一共16字节;

3、对象头包括什么

        1.8的实现,C++文件,去看Hotspot源码。

java中的对象头 jvm对象头_JVM_03

        HotSpot虚拟机的对象头(Object Header)包括两部分信息,第一部分用于存储对象自身的运行时数据, 如哈希码(HashCode)、GC分代年龄、锁状态标志、线程持有的锁、偏向线程ID、偏向时间戳等等,这部分数据的长度在32位和64位的虚拟机(暂 不考虑开启压缩指针的场景)中分别为32个和64个Bits,官方称它为“Mark Word”。

        对象需要存储的运行时数据很多,其实已经超出了32、64位Bitmap结构所能记录的限度,但是对象头信息是与对象自身定义的数据无关的额 外存储成本,考虑到虚拟机的空间效率,Mark Word被设计成一个非固定的数据结构以便在极小的空间内存储尽量多的信息,它会根据对象的状态复用自己的存储空间。例如在32位的HotSpot虚拟机 中对象未被锁定的状态下,Mark Word的32个Bits空间中的25Bits用于存储对象哈希码(HashCode),4Bits用于存储对象分代年龄,2Bits用于存储锁标志 位,1Bit固定为0,在其他状态(轻量级锁定、重量级锁定、GC标记、可偏向)下对象的存储内容如下表所示。

java中的对象头 jvm对象头_Java对象创建过程_04