MESI缓存一致性协议
前🌽:
高速缓存底层数据结构:拉链散列表的结构
bucket - cache entry - tag主内存地址 cache line缓存数据 flag缓存行状态
cache line64字节 有效引用主内存地址,连续的相邻的数据结构 读取特别快
处理器读写高速缓存,据变量名执行内存地址解码的操作,解析出:
index 定位到拉链散列表中的某个bucket
tag定位 cache entry
offerset定位变量在 cache line位置
每核CPU有自己的L1 L2缓存,多线程编程 另一核想访问当前核内L1 L2数据?
mesi缓存不命中且数据块在另一个缓存时,容许缓存到缓存的数据复制:高效
状态转换
初始:没有加载任何数据,I
本地写local write:本地处理器写数据到I的缓存行,M
本地读local read:本地处理器读I状态缓存行,此缓存没有数据可读
其他处理器缓存里没有此行数据,从内存加载数据到缓存行 设成E,只有me有
其他处理器有,此缓存行S(M状态的缓存行 由本地处理器写入/读出 状态不改)
远程读:c1 c2,c2需要c1缓存行数据,c1通过内存控制器memory controller发送c2,内存从总线存该份数据,c2将相应缓存行=S
远程写:c2得到c1数据,c2发RFO(request for owner) 拥有该份数据的权限,其他处理器相应缓存行=I 无权再操作该份数据,性能消耗大,什么情况下出现FRO,修改共享数据的时候
两个线程修改同一个cache line中不同的数据,轮番发送RFO占用缓存行拥有权,切换I ,而且其他线程读取此行数据L1 L2都是失效的,L3最新但是读取耗性能 更别说跨槽读 only内存上加载
涉及到一个伪共享问题,解决:让不同线程操作对象处于不同缓存行--》缓存行填充
缓存行64字节,java对象头markwork固定8字节(32)/12字节(64压缩不压16),填充6个无用长整形6*8字节,不同对象不同缓存行
还没完,完不了,等着我更
redis缓存三大问题:
缓存穿透:不存在的数据,布隆过滤器或者黑白名单 缓存预热
缓存击穿:热点数据失效,互斥锁/分布式锁 预热失效时快速刷新缓存
互斥锁/分布式锁:
永不过期:redis不设置过期时间,后台异步线程刷新缓存
存过期时间,快过期去更新
缓存雪崩:大量数据同时失效,随机过期时间 限流 预热 自动刷新
多级缓存 / 缓存预热 / 限流
数据一致性问题
不重要业务:先更数据库再删缓存(暂时性不一致)
缓存更新成功返回,异步线程去更新数据库(缓存挂了,数据丢失)
双写:先写myql在写redis,事务
数据同步:定期mysql同步到redis,定时任务 变更数据时触发同步
实时数据流:实时数据流 消息队列 ,mysql变更同步到redis
设计模式
工厂模式:
单例模式:
枚举
daj
kafka的ack
ack=0 不等待确认
ack=1 leader确认入盘,默认
ack=all,等待所有的副本确认,
对象头与锁状态
对象头:hotspot
markWord:存储hashCode,分代年龄 锁标志位 ,非固定结构 存储尽量多数据,动态调整,对象的状态复用自己存储空间,随着锁标志位的变化而变化
Klass point元数据指针,通过指针确定对象是哪个类的实例
无锁:25bit存对象头hashcode,4bit存放对象分代年龄,1bit是否偏向锁的标识位 2bit锁标识位01
偏向锁:25bit空间中 23bit存放线程id 2bit存放epoch 4bit分代年龄 1bit是否偏向锁标识 0无锁 1偏向锁 锁标识位01
轻量级锁:30bit指向栈中锁记录的指针 2bit锁标识位:00
重量级锁:30bit指向重量级锁的指针,2bit存放锁标识位:11
GC标记:开辟30bit内存空间没有用上,2bit存放锁标志位11
内存分配:
age_bits:分代回收的标识,4字节
lock_bits:锁标志位,2字节
biased_lock_bits:是否偏向锁标识,1字节
max_hash_bits:无锁计算的hashcode占用字节数量,32位虚拟机 32-4-2-1= 25,64位64-4-2-1=57,25字节不用,hashcode占用31位
hash_bits:64位虚拟机,最大字节>31,取31 否则真实字节数
cms_bits:不是64位虚拟机占0byte,否则1byte
epoch_bits:epoch所占有字节大小,2字节
monitor:
线程私有的数据结构,每个线程都有一个可用monitor record列表 全局可用列表;每个被锁住的对象都会和一个monitor关联,monitor有owner字段存放拥有锁的线程的唯一标识
synchronized通过对象内部监视器锁monitor实现,本质依赖底层操作系统mutex lock互斥锁实现,操作系统实现线程间切换需要用户态转换到核心态,成本高,状态转换时间长:重量级锁
-XX:-UseBiasedLocking=false来禁用偏向锁
锁分类
无锁:循环内进行修改,不断尝试修改共享资源, 只有一个能修改成功 其他循环重试
偏向锁:第一次synchronized,修改对象头锁标志位:偏向锁,执行完同步代码块后 第二次达到同步代码块 线程会判断持有锁是不是自己,是 则正常执行
线程访问同步代码块,markwork获取线程id CAS判断
撤销:等待全局安全点,没有字节码执行,暂停拥有偏向锁的线程,判断对象是否被锁定,对象头无锁状态01或00轻量级锁 撤销偏向锁
轻量级锁:关闭偏向锁/多个线程竞争偏向锁导致锁升级
锁竞争:线程尝试获取锁,该锁被占有,自旋 等待释放,cas修改对象头锁标志位
长时间自旋非常耗费资源,其他线程原地空耗cpu 执行不了有效任务:忙等
重量级锁:忙等有限度(计数器记录自旋次数10),锁竞争严重达到最大自旋次数的线程,轻量级转重量级CAS修改锁标志位不改线程id,后续线程发现是重量级锁 将自己挂起 等待唤醒
控制权交给操作系统,操作系统负责线程间调度和线程状态变更
锁的五种状态
JVM:
cpu使用过高:方法导致
磁盘IO高:栈
内存过高:堆
GC:堆
YGC:eden区满了
动态分配规则:
相同age的对象的大小之和 > 1/2的存活区大小 sum(age.mem)>=1/2S0, >=age的对象挪动到老年代