JVM知识点梳理
本文链接:https://blog.csdn.net/feather_wch/article/details/132326246
1、什么是内存模型?
2、JMM的作用是什么?
3、JMM的主要目的
1、JMM内存结构:分为主内存、工作内存,工作内存对应于高速缓存区
1、JMM的目的是什么?
2、JMM如何保证变量的并发问题
1、MESI:M-修改,E-独享互斥,S-共享,I-无效
2、缓存一致性协议是什么?
3、缓存加锁是什么?CPU的缓存写回内存会导致其他CPU的缓存失效(基于MESI)
缘由:高速缓存区(Cache)作为CPU和内存中间:数据复制入Cache中,运算结束放回内存。缓解CPU和内存速度有几个数量级的差距
多个CPU有缓存一致性问题,如果出现缓存数据不一致以谁的为准?
各个CPU采取一定协议:MSI、MESI等
1、每个线程都有私有工作内存,操作副本其他线程无法感知到,可见性问题 => volatile
线程一:
while(!initFlag){} // 循环
线程二:
initFlag = true
1、重排序分为三类
2、指令重排序为什么会导致问题?
3、JMM如何避免指令重排序?
1、重排序需要遵守as-if-serial原则
2、as-if-serial是什么?
1、JMM和HB是什么?
2、无需同步手段就能成立的HB原则,有且仅有八种
3、Happen-Before不代表时间上先发生
4、时间上先发生也不代表Happen-Before
1、JMM的构建是围绕三大原则的
2、JMM如何保证原子性的?
3、JMM如何保证有序性?
4、JMM如何保证可见性?
1、JVM内存屏障有哪些类型?
2、JVM如何实现这些屏障的
刷新写缓存指令
3、lock指令的作用
4、volatile instance的赋值,instance = xxx,内存屏障是如何做的?
StoreStore
putStatic
StoreLoad,加内存屏障
1、volatile适合什么场景?有什么限制?
1、volatile两个作用
2、volatile如何做到禁止指令重排序的?
3、volatile如何保证可见性?
1、volatile是什么?
2、volatile线程不安全
3、volatile什么情况下是线程安全的?
4、非volatile指令重排序问题
线程A:1和2交换顺序
1. 初始化
1. initFlag = true
线程B:
1. 判断 initFlag = true
1. 使用相关内容 // 会出错,根本没初始化完成
1、八大数据原子操作
存储到 工作内存: load
写入到 主内存:write
2、什么是原子操作?不可以再细分
1、线程是什么?
2、Java线程实现分为三种
1、内核线程是什么?1:1是什么意思?
2、内核线程结构是什么?
3、内核线程实现的问题是什么?
1、用户线程结构
2、问题
1、混合模式结构
2、LWP和UT共存,LWP是UT和KLT的桥梁
3、优点
1、抢占式:系统分配
2、协同式:线程工作完成后,通知系统切换
1、Java定义了六种线程状态
2、释放锁的情况
1、协程概念
2、线程和协程比较
3、协程缺点
4、oracle fiber纤程,是在JVM共存的新并发编程模型
5、Oracle fiber介绍
Atomic类
ABA
自旋时间过长
偏向锁
轻量级锁-LockRecord
自旋-自适应
锁消除
锁粗化
1、synchronized底层是什么?
2、synchronized和底层monitorenter需要一个对象参数
3、synchronized内部加锁真正的是Monitor对象
4、Monitor对象的原理
5、为什么会有EntryList?同时获得到锁的线程不是只有一个吗?
1、原子类的性能,最少高一倍多
1、原子类借助while+CAS实现 = (自旋)
2、CAS自称无锁是指真的没有锁吗?在CPU层面也是有锁的
3、CompareAndSet源码
->Unsafe.java
->Unsafe.cpp
->1. LOCK_IF_MP: 多核CPU返回lock指令 // 加锁,保证多CPU并行安全
->2. cmpxchg: // 原子比较和交换指令 Compare and Exchange
4、lock是什么?
5、lock cmpxchg的解析,为什么原子指令还需要lock?
6、CAS具有的问题
1、锁升级的流程,以及锁各状态之间如何切换?
无状态-001(默认4秒)
|-线程id写入markword(启用偏向锁)
偏向锁-101
|-多个线程轻量竞争,CAS轻量竞争
轻量锁-00
|-自旋不成功,自适应自旋也不行,重度竞争
重量级锁-10 // markword指向monitor
无状态-001
|-未启用偏向锁
轻量锁-00
偏向锁-101
|-调用wait,直接进入重量级锁。重量级锁才有的状态。 ====> wait
重量级锁-10
2、JVM默认4秒后自动开启偏向锁
3、偏向锁或者无锁进入轻量级锁的检查流程
4、锁升级流程
5、CAS自旋10次,或者可能自适应自旋2~3次,进入重锁
6、分代年龄等信息暂存在其他地方,会恢复。
1、LongAdder用于高并发下替换Atomic类, 高并发计数器
2、LongAdder原理
1、双重检查加锁的对象半始化问题
1、下面字节码指令导致的效果是怎么样的?
地址 | 指令 |
---|---|
0 | ICONST_1 |
1 | ISTORE 0 |
2 | ICONST_2 |
3 | ISTORE 1 |
4 | ILOAD 0 |
5 | ILOAD 1 |
6 | ADD |
7 | ISTORE 2 |
8 | RETURN |
阐述在PC、操作数栈、局部变量表中是如何变换的
非虚
虚->守护内联
->内敛缓存
->单态
->多态
-进程层面
-JVM性能监控
1、方法区
2、运行时常量池
3、符号引用有哪些?
3、具有动态性:String的intern方法
2、堆
2、为什么要年龄划分?
1、虚拟机栈是什么?
2、栈帧StackFrame里面是什么?
3、局部变量表存放的是什么?
4、压缩指针是什么?
1、PC
1、直接内存是什么?
1、对象内存布局
2、Markword
3、类型指针:指向类型元数据(方法区)
1、什么场景下会有new指令
1、对象创建流程
2、对象创建流程中,new指令对应于1,2,3,4
3、volatile,在对象创建流程中,123456步骤上下会插入monitorEnter和monitorExit?
TLAB、
父类private隐藏字段也占据空间
1、对象的内部结构
1、对象头组成部分
2、对象头的MarkWord包含哪些数据?
3、Markword在不同锁状态下的内容
无锁 | hashcode | 对象分代年龄 | 0 | 01 |
偏向锁 | thread id | 对象分代年龄 | 1 | 01 |
轻量级锁 | 指向栈中LockRecord的指针 | 00 | ||
重量级锁 | 指向重量级锁的指针 | 10 | ||
GC | 11 |
4、为什么起始地址需要是8字节的整数倍?
5、如何打印对象的内部组成?
ClassLayout.parseInstance(user).toPrintable()
6、字节序
===> HashMap浪费空间