关于java对象内存布局的面试题

问题:
1.解释一下对象的创建过程?
2.对象在内存中的存储布局?Object o = new Object()在内存中占用多少字符?
3.对象头具体包括什么?
4.对象怎么定位?

1.解释一下对象的创建过程?

T t = new T();

首先创建对象的时候,会先检查对象的class类有没有加载过,如果没加载过就执行类加载过程

  1. class loading(加载)

    1. 通过一个类的全限定名来获取类的二进制字节流
      并未指定总哪获取,怎么获取。所以字节流可以是存储在硬盘上的文件,可以是运行时动态生成的二进制字节流,可以是有其他文件生成的(JSP对应的class文件)等等
    2. 将字节流代表的静态存储结构转化为方法区的运行时数据结构
    3. 在内存中生成一个代表这个类的Class对象,作为方法区这个类的访问入口
  2. class linking(verification、preparation、resolution)

    1. Verification (验证)

      ​ 校验装载的class文件是否符合JVM规范

    2. Preparation(准备)

      ​ 将class文件的静态变量赋默认值

      ​ 如:

      ​ static int i = 8 ;

      ​ 这里会给i赋值0,而不是赋值8。

    3. Resolution(解析)

      1. 将类、方法、属性等符号引用解析为直接引用

      ​ 对class文件常量池中各种符号引用进行解析,转成指针,偏移量等内存地址的直接引用

  3. class initializing(初始化)

    1. 给类静态变量设置初始值,同时执行静态语句块.
  4. 申请对象内存

  5. 成员变量赋默认值

    1. 给类成员变量 比如 int i =8;

      将i赋值为0; i=0;

  6. 调用构造方法

    1. 成员变量顺序赋初始值

      i =8;

    2. 执行构造方法

      super();一旦调用构造方法就先会执行父类初始化过程

2.对象在内存中的存储布局?Object o = new Object()在内存中占用多少字符?

  1. 对象的大小以及内存布局与虚拟机的实现和设置有很大关系,所以先查看虚拟机的默认配置信息

  2. 观察虚拟机配置

    java -XX:+PrintCommandLineFlags -version
    在这里插入图片描述
    -XX:InitialHeapSize 初始的堆大小

    -XX:MaxHeapSize 最大的堆大小

    -XX:+UseComparessedClassPointers 和对象头有关系

    -XX:+UseCOmparessedOops 和对象内存布局有关系

    -XX:+UseParallelGC 和GC有关系

  3. 对象的内存布局可以分为两种对象普通对象和数组对象

    普通对象

    ​ 1.对象头:在虚拟机中称为(markword),8个字节

    ​ 2.ClassPointer指针(类指针,指向这个对象对应的T.class对象):

    ​ -XX:+UseCompressedClassPointers 开启的话是4个字节,不开启则为8个字节

    ​ 3.实例数据(成员变量)

    ​ 引用类型:-XX:+UseCOmpressedOops 开始的话4个字节,不开启则为8个字节

    ​ 基本数据类型:

    ​ 4.Padding对齐,8的倍数。

    ​ 假如这个对象前面几项占用的字节为15则会补1个字节,则这个对象占用16字节

    数组对象(比普通对象多一个数组长度)

    ​ 1.markword 8个字节

    ​ 2.ClassPointer指针 4/8个字节

    ​ 3.数组长度:4个字节

    ​ 3.数组数据

    ​ 4.Padding对齐,8的倍数。

    ​ 假如这个对象前面几项占用的字节为15则会补1个字节,则这个对象占用16字节

  4. 实验——观察对象的大小

    使用技术:java Agent

    JavaAgent 是JDK 1.5 以后引入的,也可以叫做Java代理。

    JavaAgent 是运行在 main方法之前的拦截器,它内定的方法名叫 premain ,也就是说先执行 premain 方法然后再执行 main 方法。

    class文件在loading到内存中的时候,可以通过javaAgent做代理,抓取class的二进制字节码进行操作。

    1.新建项目ObjectSize

    1. 新建项目ObjectSize (1.8)

    2. 创建文件ObjectSizeAgent

      package com.ls.jvm.agent;
      
      import java.lang.instrument.Instrumentation;
      
      /**
       * Created by 刘绍 on 2020/2/16.
       */
      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);
          }
      }
      
      
    3. src目录下创建META-INF/MANIFEST.MF

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

      注意Premain-Class这行必须是新的一行(回车 + 换行),确认idea不能有任何错误提示

    4. 打包jar文件

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

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

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

    7. 如何使用该类:

      import com.ls.jvm.agent.ObjectSizeAgent;
      
      public class SizeOfAnObject {
          
          
          public static void main(String[] args) {
              System.out.println(ObjectSizeAgent.sizeOf(new Object()));//16
              
              /*对象头8个字节+类指针4个字节,由于默认开启了-XX:+UseComparessedClassPointers 
              所以是4个字节,成员变量没有所以对象字节为8+4=12 最后Padding对齐,最后结果为16字节*/
              
              System.out.println(ObjectSizeAgent.sizeOf(new int[] {}));//16
              /*对象头8个字节+类指针4个字节,由于默认开启了-XX:+UseComparessedClassPointers 
              所以是4个字节,数组长度4个字节,成员变量没有所以对象字节为8+4+4=16。结果是8的倍数所以		不需要最后Padding对齐,最后结果为16字节*/
              
             	/*在执行main方法的时候加入参数 -XX:-UseComparessedClassPointers 关闭类指针压缩
             	ObjectSizeAgent.sizeOf(new Object()) 为16字节
             	对象头8个字节+类指针8个字节=16字节 
             	
             	ObjectSizeAgent.sizeOf(new int[] {})
              对象头8个字节+类指针8个字节+数组长度4个字节+Padding对齐4个字节 = 24字节*/
              
              System.out.println(ObjectSizeAgent.sizeOf(new P()));//32
              
              //对象头8个字节+类指针4个字节+4+4+4+1+1+4+1+Padding对齐1个字节 = 24字节
            
          }
      
      	private static class P {
              //8 _markword
              //4 _oop指针
              
              //-XX:+UseCOmparessedOops 成员变量类型引用指针。默认开启4字节,不开启为8字节
              int id;         //4
              String name;    //4
              int age;        //4
      
              byte b1;        //1
              byte b2;        //1
      
              Object o;       //4
              byte b3;        //1
      
          }
      }
      
    8. Hotspot开启内存压缩的规则(64位机)

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

3.对象头具体包括什么?

包括存储对象自身的运行时数据:哈希码(HashCode)、GC分代年龄、锁状态标志、线程持有的锁、偏向线程ID、偏向时间戳等
在这里插入图片描述
在这里插入图片描述
1.其中可以看到不管32位还是64位中 GC的分代年龄只有4bit,4bit最大的数字为15。所以新生代进入老年代中最大的年龄限制就是15

2.当一个对象计算过identityHashCode之后,不能进入偏向锁状态

​ 参考资料:

​ https://cloud.tencent.com/developer/article/1480590

​ https://cloud.tencent.com/developer/article/1484167

​ https://cloud.tencent.com/developer/article/1485795

​ https://cloud.tencent.com/developer/article/1482500

4.对象怎么定位?

(深入java虚拟机第二版 第48页)

建立对象是为了使用对象,我们通过栈上的reference数据来操作堆上的具体对象。

由于reference类型在java虚拟机规范中只规定了一个指向对象的引用,并没有定义引用应该通过什么方式定位、访问堆中的对象具体位置,所以对象的访问方式取决于虚拟机的实现。

主流的访问方式有两种,HotSpot采用的是直接访问

1.句柄访问

好处:reference中存储的是稳定的句柄地址,在对象被移动(垃圾收集时移动对象是非常普遍的行为)时只会改变句柄中的实例数据指针,而reference本身不会改变

在这里插入图片描述

2.指针访问

好处:访问速度更快,它节省了一次指针定位的时间开销

在这里插入图片描述

  • 3
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值