JVM内存管理JMM

前面讲到了类加载器,咱们知道都知道类是需要加载到内存中的,这个类在内存需要占用多少内存呢?当咱们调用某个方法内存JVM是怎么申请内存,怎么保证数据一致性的呢?

数据一致性

了解数据一致性之前,先了解下CPU。
在这里插入图片描述

ALU:计算单元
Registers:寄存器组,数据需要存储在寄存器组
PC:寄存器用于存储CPU需要执行的指令
chache:用于存储线程切换的指令和数据
ContextSwitch:线程上线文切换
缓存:
	L1、L2、L3(CPU共享)

为什么要了解数据一致性呢?随着科技的发展,咱们很少见到只有单核的服务器了,大多都是多核心(这个补充一个小的知识点,一个CPU有多个核心,通过Context Switch上下文切换就是超线程)。这样就暴露一个问题,多核心同时修改主存的某一个数据,它们是怎么做到的呢???

先说下答案:缓存锁(MESI…)+ 总线锁(buslock)
为什么需要缓存锁加上总线锁呢?只有缓存锁能实现吗?只有总线锁呢?

咱们先了解下CPU操作数据的流程:当程序接受到数据加载的命令后先去L1去查找数据,如果L1找到了直接返回,如果没有找到,继续去L2查找,如果L2没有找到,继续去L3查找,L3查找不到就会去主存进行加载数据。

CPU为什么这么设计呢?咱们看一张图就明白了。访问延迟开销差距很大
在这里插入图片描述

在这里插入图片描述

讲到这里可能有人疑问,这跟数据一致性有什么关系呢?
这里需要强调一点,CPU读取数据是以cache line为基本单位,目前64byte。
那可能有人就有疑问了:位于同一缓存行的两个不同数据,被多个不同CPU锁定,是不是产生相互影响?
答案是肯定会相互影响的,而且使用缓存行对齐能够提高效率。
比如:一个int数组{1,2,3},CPU加载的时候需要128byte的内存,如果这个数组元素是2的倍数,就可以完全加载,不需要对齐。

刚刚提到锁定一词,它们是如何进行保证缓存与主存一致性的呢?
先了解下一MESI缓存一致性协议

乱序问题

CPU为了提高执行效率,会在一条指令执行过程中(比如去内存读数据(要慢100倍)),去执行另一条,前提是,两条指令没有依赖关系。
比如

int var1 = 1;
int var2 = 2;
int var3 = var1 + var2;
//CPU在加载var1和var2的时候不一定var1先行加载。
//但是加载var3的时候却需要先加载完var1和var2.

具体大家可以看一个案例

内存屏障

硬件层面

sfence:store|在sfence指令前的写操作必须在sfence指令后的写操作前完成
lfence:load|在lfence指令前的读操作必须在lfence指令后的读操作前完成。
mfence:mix| 在mfence指令前的读写操作必须在mfence指令后的读写操作前完成。
原子指令,如x86上的“lock…”指令是一个Full Barrier,执行是会锁住内存子系统来确保执行顺序,甚至跨多个CPU,Software Locks通常使用了内存屏障或原子指令来实现变量可见性和保持顺序

JVM如何规范

LoadLoad屏障:
例子: load1-->LoadLoad-->load2
保证Load1在Load2被读取之前读取完数据
StoreStore屏障:
例子: store1-->StoreStore-->store2
保证Store1的写入操作在Store2前执行完成。即保证Store1的写入操作在后续操作中其他处理器可见。
LoadStore屏障:
例子: load1-->LoadStore-->store2
保证Store2及后续写入操作被刷出前,保证Load1要读取的数据读取完毕
StoreLoad屏障:
例子: store1-->StoreLoad-->load2
在load2及后续的读操作执行前,保证Store1的写入对所有处理器可见。

对象的创建过程

1、class loading
2、class linking(verification,preparation,resolution)
3、class initializing
4、申请对象内存
5、成员变量赋默认值
6、调用构造方法
1、成员变量顺序赋初始值
2、执行构造方法语句

对象在内存中的布局

打印参数java -XX:+PrintCommandLineFlags -version
普通对象
1、对象头: markword 8字节
2、classPointer指针:-XX:+UseCompressedClassPointers 为4字节 不开启为8字节
3、实例数据
		OOPS:Ordinary Object Pointers
4、padding对齐 8的倍数(不够64字节补齐)
数组对象
1、对象头:markword 8
2、classPointer指针
3、数组长度:4字节
4、数组数据
5、对齐 8的倍数
IdentityHashCode的问题
当一个对象计算过IdentityHashCode之后,不能进入偏向锁状态
对象怎么定位
1、句柄池:引用句柄池(包括两个引用,一个指向内存,一个指向class对象)GC效率比较高
2、直接指针:直接指针指向内存(HotPot)

本博客用于技术学习,所有资源都来源于网络,部分是转发,部分是个人总结。欢迎共同学习和转载,转载请在醒目位置标明原文。如有侵权,请留言告知,及时撤除。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值