JVM
2022-04-29 08:02:38 16 举报
AI智能生成
登录查看完整内容
为你推荐
查看更多
JVM知识点梳理
作者其他创作
大纲/内容
L0:寄存器
L1:高速缓存
L2:高速缓存
L3:高速缓存
L4:主内存
L5:硬盘
L6:远程文件存储
更小 更快 成本更高---更大 更慢 成本更低
存储器的层次结构
单个核的内部共享
数据参与计算前存放的地址
所有CPU共享
单个CPU内部所有核共享
cpu从内存中读取数据以cache line为基本单位,目前长度为64bytes
位于同一缓存行的两个不同数据,被两个不同的CPU锁定,产生互相影响
伪共享
每次需要读取的数据独占64byte字节
使用缓存行的对齐能够提高效率
缓存行
S-shared---该状态意味着当前缓存行数据不止存在本地CPU缓存中,还存在别的CPU的缓存中。这个状态的数据和内存中的数据是一致的
E-exclusive---代表该缓存行对应内存中的内容只被该CPU缓存,其他CPU没有缓存该缓存行中的内容
I-invalid---代表该缓存行中的内容是无效的
缓存一致性协议
缓存锁
CPU访问主内存时加锁
总线锁
硬件层数据的一致性
现代CPU的数据一致性实现 = 缓存锁(MESI ...) + 总线锁
CPU为了提高指令执行效率,会在一条指令执行过程中,去同时执行另一条指令,前提是,两条指令没有依赖关系
各级缓存读写速度相差巨大
产生的原因
当读取的指令在数据上没有依赖关系是,读取指令乱序执行
乱序读
什么是乱序执行
Write Combining Buffer 是CPU里面真实的存储单元,是硬件
Intel的CPU中,Write Combining=4byte
合并写
save fence
load fence
mfence
CPU内存屏障
x86指令
Lock指令
硬件层面解决乱序问题
LoadLoadBarrier
LoadStoreBarrier
StoreStoreBarrier
StoreLoadBarrier
JVM层面解决乱序问题
Access flags:volatile
字节码层面
StoreStoreBarriervolatile 写操作StoreLoadBarrier
LoadLoadBarriervolatile 读操作LoadStoreBarrier
volatile内存区的读写都加屏障
JVM层面
lock指令保证顺序执行
MESI协议保证可见性
OS和硬件层面
volitile实现细节
方法:Access flags:synchronized同步代码块:monitorenter/monitorexit
C/C++调用了操作系统同步机制
X86:lock 指令实现
硬件层面
synchornized实现细节
乱序执行
loading
校验class文件
静态变量赋默认值
符号引用变为直接引用
linking
静态变量赋初始值
initializing
字节码load到内存
指针碰撞
空闲列表
申请内存
选择哪种分配方式是由 Java 堆是否规整来决定的,而 Java 堆是否规整又由所采用的垃圾收集器是否带有压缩整理功能决定。
成员变量赋默认值
成员变量顺序赋初始值
执行构造方法语句
调用构造方法
对象创建过程
MarkWord(HotSpot对象头):8byte
ClassPointer(指针):指向class对象
INSTANCE(实例数据):成员变量字节数
Padding(对齐):8的倍数
普通对象
数组长度:4byte(比普通对象多了一个数组长度)
数组对象
对象在内存中的存储布局
ClassPointer指针:-XX:+UseCompressedClassPointers 指针压缩开启时,为4byte,关闭时为8byte
Ordinary Object Pointers普通对象指针:XX:+UseCompressedOops 指针压缩开启时,为4byte,关闭时为8byte
观察虚拟机配置---java -XX:+PrintCommandLineFlags -version
对象头总共8byte估计64bit
根据对象的不同状态头的布局是不同的
4bit分带年龄
2bit表示锁标志位
1bit表示偏向锁
对象头具体包括什么
reference中存储的就是对象的句柄地址
句柄池
使用直接指针方式最大的好处就是速度更快,他节省了一次指针定位的时间开销
reference中直接存储的就是对象地址
直接指针(HotSpot)
超链接
对象定位
16字节-无论是否开启ClassPointer指针压缩,都是16字节,因为有Padding对齐8的倍数
Object o = new Object()在内存中占多少字节
线程私有
小对象
只在某段代码中使用
无逃逸
可以用一些基本的属性直接替换对象,比如一个对象里面只有1个int类型的变量
支持标量替换
无需调优
栈上分配
占用Eden1%
多线程的时候不用竞争eden区域,就可以申请空间,提高效率
线程本地分配(Thread Local Allocation Buffer-TLAB)
Eden区
Old区
对象怎么分配
可以调整JVM参数进行关闭
对象
JMM
new
将位于栈顶的值复制1份放入栈顶
dup
invokespecial
将栈顶的值弹出赋值给1位置的局部变量
astore_1
T t = new T();
静态方法调用
InvokeStatic
调用普通方法
InvokeVirtual
调用接口
InvokeInterface
调用private 方法 , 构造方法
InovkeSpecial
JVM最难的指令:lambda表达式或者反射,动态产生的Class,会用到的指令
InvokeDynamic
invoke
JVM Instructions
Operand Stacks(操作数栈)
存放:\t1)方法参数m(params) \t2)本地变量\t3)如果是非static方法,还会存放this
VariablesTable(局部变量表)
将符号引用解析为直接引用的过程
动态链接
Dynamic Linking(动态链接)
遇到返回的字节码指令
异常退出
Return Address(动态返回地址)
frame(栈帧)
为执行Java方法服务
JVM Stacks(虚拟机栈)
为虚拟机使用到的Native方法服务
Native Method Stacks(本地方法栈)
当前线程所执行的字节码的行号指示器
Progarm Counter(程序计数器)
Stacks(栈)
方法运行的基本单位- 每个方法对应一个栈帧
Sun公司的HotSpot虚拟机,直接把本地方法栈和虚拟机栈合二为一
1.8之前的实现,叫做:永久代
FGC不会清理
字符串常量位于Permanent Generation
大小启动的时候指定,不能变
Permanent Generation
1.8之后的实现,叫做:元空间
字符串常量位于堆
会触发FGC清理
不设定的话,最大就是物理内存
Metadata Space
Runtime Constant Pool
这个类型的完整有效名
这个类型直接父类的完整有效名(除非这个类型是interface或是java.lang.Object,两种情况下都没有父类)
这个类型直接接口的一个有序列表
类型的常量池( constant pool)
类的静态变量
类变量
类型信息
域(Field)信息
方法(Method)信息
对类加载器的引用
对Class类的引用
存储信息
Method Area(方法区)
所有线程共享
Heap(堆)
Direct Memory(堆外内存)
JVM Runtime Data Area
Oracle官方 1.8之后开始收费
Hotspot
BEA,曾经号称世界上最快的JVM,被Oracle收购,合并于Hotspot
Jrocit
Hotspot深度定制版
TaobaoVM
直接针对硬件
LiquidVM
最新垃圾回收的业界标杆(收费)
azul zing
常见的实现
任何语言只要能被编译成class文件,符合虚拟机规范都能丢到jvm上执行
jvm与class文件格式
jvm是一种规范,是虚构出来的一台计算机
JVM<JRE<JDK
JVM基础
是虚拟机自身的一部分,用来加载Java_HOME/lib/目录中的,或者被 -Xbootclasspath 参数所指定的路径中并且被虚拟机识别的类库;
BootstrapClassLoader
负责加载\\lib\\ext目录或java. ext. dirs系统变量指定的路径中的所有类库
ExtClassLoader
负责加载用户类路径(classpath)上的指定类库,我们可以直接使用这个类加载器。一般情况,如果我们没有自定义类加载器默认就是用这个加载器
AppClassLoader
继承abstract class ClassLoader
重写findClass(String name)方法
自定义的ClassLoader的parent属性是AppClassLoader
自定义ClassLoader
分类
System.getProperty(\"sun.boot.class.path\")
System.getProperty(\"java.ext.dirs\")
System.getProperty(\"java.class.path\")
类加载器范围
自底向上检查该类是否已经加载 -->parent方向(缓存中查找)
自顶向下进行实际查找和加载 -->child方向(实际加载)
双亲委派,主要出于安全来考虑
重写loadClass( )
JDK1.2之前,自定义ClassLoader都必须重写loadClass()
热启动,热部署
ThreadContextClassLoader可以实现基础类调用实现类代码,通过thread.setContextClassLoader指定
何时打破
双亲委派的打破
双亲委派机制
流程图
loadClass--->findLoadedClass--->parent.loadClass---> findClass ---> defineClass
ClassLoader源码执行过程
隐式加载
显示加载
类装载方式
JVM规范并没有规定何时加载
在遇到 new、getstatic、putstatic 或者 invokestatic 这四条字节码指令时
子类加载前,先加载父类
在使用 java.lang.reflect 包的方法进行反射调用的时候
加载时机
lazyloading
ClassLoader
混合执行
编译执行
解释执行
执行模式
Loading
验证文件是否符合JVM规定
Verification
Preparation
虚拟机将常量池中的符号引用替换成直接引用的过程。符号引用就理解为一个标识,而直接引用就是直接指向内存中的地址
Resolution
Linking
类的静态变量赋初值值
初始化子类前先初始化父类
调用类初始化代码 <clinit>
Initializing
Class加载-初始化
没有任何引用指向的一个对象或者多个对象
什么是垃圾
无法解决循环引用的问题
给对象中添加一个引用计数器,每当有一个地方引用它时,计数器值加1;当引用失效时,计数器值减1,引用数量为0的时候,则说明对象没有被任何引用指向,可以认定是“垃圾”对象
引用计数(Reference Count)
JVM栈中引用的对象(栈帧中的本地变量表)
方法区中,静态属性引用的对象
方法区中,常量引用的对象
本地方法栈中,JNI(即Native方法)引用的对象
GC Roots 的对象作为根
当一个程序起来之后马上需要的对象叫做根对象
从根上开始向下搜索,搜索所走过的路径称为引用链(Reference Chain),当一个对象到 GC Roots 没有任何引用链相连时,则证明此对象是不可用的
根可达算法(Root Searching)
如何定位垃圾
使用Root Serching找到有用的对象
清除垃圾对象
扫描2次内存
不适用于Eden区,因为Eden区存活的对象很少,如果采用MS会产生大量的内存碎片
会产生内存碎片
老年代
不需要挪动对象
不存在内存浪费
Mark-Sweep 标记清除
将内存分为两个区域from/to,使用Root Serching找到有用的对象,边找边拷贝B区域,拷贝完成后直接清除A
扫描1次内存
没有内存碎片
效率高
适用于Eden
需要挪动对象
内存浪费
Copying 拷贝算法
找到有用的对象
把对象往前移动,移动的过程中,如果是多线程还要考虑线程同步,所以效率较低
需要扫描2次内存
Mark-Compact 标记压缩
垃圾回收算法
对象刚创建
Survivor1(From)
Survivor2(to)
很快被回收的对象
默认比例一般为---8:1:1
年轻代
参数-XX:MaxTenuringThreshold 配置,默认15
超过指定年龄
超过-XX:PretenureSizeThreshold 设置的字节数
大对象
Hotspot遍历所有对象时,按照年龄从小到大对其所占用的大小进行累积,当累积的某个年龄大小超过了survivor区的一半时,取这个年龄和MaxTenuringThreshold中更小的一个值,作为新的晋升年龄阈值
动态年龄判断规则进入
YGC正在发生时,Survivor区放不下,超过的部分转移到老年代
分配担保
老年代(Tenured/Old)
该类位于堆中的class对象没有任何引用
该类的Class对象的实例对象已经从堆内存被回收
该类ClassLoader已经被回收
指的就是方法区(存放Class元数据),回收条件较苛刻
永久代
JVM中的内存分代模型
单线程清理
内存非常小的情况<100M
Serial
Mark-Compact
Serial Old
对比Serial是多线程清理的
吞吐量优先
Parallel Scavenge(PS)
Parallel Old(PO)
对PS做了些增强,以便和CMS配合使用
响应时间优先
ParNew
诞生的原因就是因为无法忍受STW
STW
初始标记(单线程)
并发标记
由于并发标记时工作线程还在执行,会产生新的垃圾
重新标记(多线程)
此时依然有工作线程在运行,因此会产生浮动垃圾,这部分的垃圾只能进入下一次CMS再清理
并发清理
阶段
浮动垃圾
触发FullGC时转变成Serial Old采用Mark-Compact整理碎片!一定要避免
问题
–XX:CMSInitiatingOccupancyFraction=n% 可以降低这个值,提早触发CMS
降低触发CMS的阈值
升级CPU
调大内存
保持老年代有足够的空间
解决方式
黑色
灰色
未被标记过的对象
白色
三色标记
发生于ConcMarkSweep阶段
1.黑色对象直接指向白色对象
2.灰色对象指向白色对象的引用消失了
两个必要充分条件
漏标
Remark阶段
Increament Update
漏标问题的解决
标记清除算法
CMS(ConcurrentMarkSweep)
Eden
Survivor
Old
Humongous
Region
优先回收那些花费时间短,垃圾对象多的Region
分代逻辑
CardTable
CSet(Collection)
RSet(RememberedSet)
基本概念
新老年代比例
加大内存
Cpu升级
初始标记(STW)
最终标记(STW)
筛选回收(STW)
MixGC(相当于一套CMS)
-XX:InitiatingHeapOccupacyPercent
可以通过参数设定降低触发MixGC阈值(默认是45%)
降低MixGC的阈值
如何避免FullGC
关于G1产生的FullGC
为被标记过的对象
Remark阶段解决
将灰色对象指向白色对象消失的引用push到GC线程堆栈中
SATB(Snapshot At The Begining)
为什么使用SATB
标记压缩算法
ColoredPointer(颜色指针)
算法
G1
ZGC
Shenandoah
Eplison
聊聊Java的GC机制
外框
常见用的垃圾收集器
GC&GC Tuning
JVM
0 条评论
回复 删除
下一页