首页
学习
活动
专区
圈层
工具
发布
社区首页 >专栏 >让你吊打面试官的Java内存结构以及对象的内容讲解

让你吊打面试官的Java内存结构以及对象的内容讲解

作者头像
@派大星
发布2023-06-28 11:07:22
发布2023-06-28 11:07:22
2380
举报
文章被收录于专栏:码上遇见你码上遇见你

new 对象的过程:申请内存空间->默认值->初始值

这里有可能发生指令重排 出现半初始化状态 DCL单例为什么要加volatile的答案(new Instance会出现问题)

存储器的层次结构:

硬件层数据一致性

协议很多

intel 用MESI

https://www.cnblogs.com/z00377750/p/9180644.html

现代CPU的数据一致性实现= 缓存锁(MESI ...) + 总线锁

读取缓存以cache line为基本单位,目前64bytes

位于同一缓存行的两个不同数据,被两个不同CPU锁定,产生互相影响的伪共享问题

使用缓存行的对齐能够提高效率(disruptor 框架就是利用了这一点)

乱序问题(读):

CPU乱序执行的问题:如果没有直接依赖关系 为了提高效率 在一条指令执行的过程中(读数据慢100倍),去同时执行另外一条指令

WCBuffer(4个位置):写操作也可以进行合并(放在WCBuffer执行)

案例:

代码语言:javascript
复制
packagecom.mashibing.juc.c_029_WriteCombining;
 
public final class WriteCombining {
 
   private static final int ITERATIONS = Integer.MAX_VALUE;
   private static final int ITEMS = 1 << 24;
   private static final int MASK = ITEMS - 1;
 
   private static final byte[] arrayA = new byte[ITEMS];
   private static final byte[] arrayB = new byte[ITEMS];
   private static final byte[] arrayC = new byte[ITEMS];
   private static final byte[] arrayD = new byte[ITEMS];
   private static final byte[] arrayE = new byte[ITEMS];
   private static final byte[] arrayF = new byte[ITEMS];
 
   public static void main(final String[] args) {
 
       for (int i = 1; i <= 3; i++) {
           System.out.println(i + " SingleLoop duration (ns) = " +runCaseOne());
           System.out.println(i + " SplitLoop  duration (ns) = " + runCaseTwo());
       }
    }
 
   public static long runCaseOne() {
       long start = System.nanoTime();
       int i = ITERATIONS;
 
       while (--i != 0) {
           int slot = i & MASK;
           byte b = (byte) i;
            arrayA[slot] = b;
           arrayB[slot] = b;
           arrayC[slot] = b;
           arrayD[slot] = b;
           arrayE[slot] = b;
           arrayF[slot] = b;
       }
       return System.nanoTime() - start;
    }
 
   public static long runCaseTwo() {
       long start = System.nanoTime();
       int i = ITERATIONS;
       while (--i != 0) {
           int slot = i & MASK;
           byte b = (byte) i;
           arrayA[slot] = b;
           arrayB[slot] = b;
           arrayC[slot] = b;
       }
       i = ITERATIONS;
       while (--i != 0) {
           int slot = i & MASK;
           byte b = (byte) i;
           arrayD[slot] = b;
           arrayE[slot] = b;
           arrayF[slot] = b;
       }
       return System.nanoTime() - start;
    }
}

如何保证特定情况下不乱序

JVM内存屏障(有序性)硬件层内面上的实现并不一定是依赖硬件级别内存屏障实现还可以依赖硬件级别的lock指令

硬件CPU内存屏障

sfence: store| 在sfence指令前的写操作当必须在sfence指令后的写操作前完成。

lfence:load | 在lfence指令前的读操作当必须在lfence指令后的读操作前完成。

mfence:modify/mix | 在mfence指令前的读写操作当必须在mfence指令后的读写操作前完成。

JVM级别如何规范(JSR133):

LoadLoad屏障

对于这样的语句Load1;LoadLoad; Load2,

在Load2及后续读取操作要读取的数据被访问前,保证Load1要读取的数据被读取完毕。

StoreStore屏障:

对于这样的语句Store1;StoreStore; Store2,

在Store2及后续写入操作执行前,保证Store1的写入操作对其它处理器可见。

LoadStore屏障:

对于这样的语句Load1;LoadStore; Store2,

在Store2及后续写入操作被刷出前,保证Load1要读取的数据被读取完毕。

StoreLoad屏障: 对于这样的语句Store1; StoreLoad;Load2,

在Load2及后续所有读取操作执行前,保证Store1的写入对所有处理器可见。

Volatile的实现细节:

1. 字节码层面:

ACC_VOLATILE

2. JVM层面:

Volatile内存区的读写都加屏障

StoreStoreBarrier

volatile 写操作

StoreLoadBarrier

LoadLoadBarrier

volatile 读操作

LoadStoreBarrier

3. OS和硬件层面

https://blog.csdn.net/qq_26222859/article/details/52235930

hsdis- HotSpot Dis Assembler

windows lock 指令实现 | MESI实现

synchronized实现细节:

1. 字节码层面:

ACC_SYCHRONIZED

moitorenter monitorexit(2个)

2. JVM层面

C++调用了操作系统提供的同步机制

3. OS和硬件层面

X86 : lock cmpxchg / xxx

https://blog.csdn.net/21aspnet/article/details/88571740

对象的创建过程:

1. classloading

2. classlinking(verification preparation resolution)

3. classinitializing

4. 申请对象内存

5. 成员变量赋默认值

6. 调用构造方法<init>

成员变量顺序赋初始值

执行构造方法语句

对象在内存的存储布局:

首先查看自己虚拟机的配置:java-XX:+PrintCommandLinesFlags -version

红色的参数就是内存布局

对象的内容(对象的内存部局也可以这么说):

普通对象

1. 对象头markword8

2. ClassPointer指针 XX:+UseCompressedClassPointers 为4字节 不开启为8字节

3. 实例数据引用类型:-XX:+UseCompressedOops为4字节 不开启为8字节Oops Ordinary Object Pointers

4. Padding对齐 8的倍数

数组对象:

1. 对象头markword8

2. ClassPointer指针 XX+UserCompresseddClassPointer为4字节 不开启为8字节

3. 数组长度:4字节

4. 数组数据

5. 对其8的倍数

首先一个class load到内存的时候有一个agent 来截获或者修改 需要自己实现它(premain 方法) 相当于代理

实验:

1. 新建项目ObjectSize(1.8)

2. 创建文件ObjectSizeAgent

代码语言:javascript
复制
packagecom.mashibing.jvm.agent;
 
importjava.lang.instrument.Instrumentation;
 
public classObjectSizeAgent {
    private static Instrumentation inst;
 
    public static void premain(StringagentArgs, 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. 在需要使用该AgentJar的项目中引入该Jar包 project structure- project settings - library 添加该jar包

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

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

7. 如何使用该类:

代码语言:javascript
复制
packagecom.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(newP()));
       }
   
       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字节

Int 16字节

// -XX:+UseCompressedClassPointers-XX:+UseCompressedOops 这两个是分开的

// Oops = ordinary object pointers

Hotspot开启内存压缩的规则(64位机):

1. 4G以下,直接砍掉高32位

4G - 32G,默认开启内存压缩ClassPointers Oops (ordinary object pointers)

2. 32G,压缩无效,使用64位 内存并不是越大越好(^-^)

对象头具体包含什么:

具体看对象的状态

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

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2021-04-17,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 码上遇见你 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 存储器的层次结构:
  • 硬件层数据一致性
  • 乱序问题(读):
  • 如何保证特定情况下不乱序
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档