JVM脑图
2022-12-21 17:07:11 0 举报
AI智能生成
凌风的技术小屋-JVM脑图
作者其他创作
大纲/内容
内存
内存分布
运行时数据区
线程栈
局部变量表
操作数栈
动态链接
方法出口
本地方法栈
程序计数器
字节码执行行号
方法区
编译后代码
类信息
静态变量
常量
运行时常量池
Integer== -128 127 问题
数据堆
几乎所有“对象”
直接内存
DirectByteBuffer
内存分配
对象创建
1.常量池检查类是否加载
2.内存分配
指针碰撞
整块内存区域划分 【serial、parNew】(新生代)
空闲列表
碎片化内存,通过指针列表管理【CMS 标记-清除】
3.对象指针线程并发安全
采用CAS同步处理,成功的线程获得对象指针
(Thread Local Allocation Buffer)TLAB
每个线程在Java堆中分配一小块内存,
TLAB用完后,在使用CAS同步
-XX:+/-UserTLAB 开关
TLAB用完后,在使用CAS同步
-XX:+/-UserTLAB 开关
4.对象头信息设置,类关联,属性值归零
5.<init> 用户构造函数调用
对象布局
MarkWord
实体数据
padding
内存回收(GC)
寻找垃圾 算法
引用计数法
优点:简单
缺点:循环引用不可回收
引用 可达性分析法
算法
GC Root 根节点开始形成引用连
对象到GCRoot没有任何引用链,对象不可达,可以回收
对象到GCRoot没有任何引用链,对象不可达,可以回收
GCRoot:
- 虚拟机栈中本地变量表中的对象:参数、局部变量
- 方法区中静态属性引用的对象
- 方法区中常量引用的对象,常量池中的对象
- 所有被同步锁(synchronized)持有的对象
- 本地方法栈Native方法引用的对象
- 常驻Class对象:异常对象,系统类加载器对象
并发可达性分析[三色标记]
白色
没有被访问的对象
黑色
已经访问过,并且对象到的所有引用都访问过
灰色
访问过,但至少还有一个引用没访问;全部访问完毕转为黑色
并发情况
多标【浮动垃圾】
多标:在扫描过程中黑色断开了引用
问题:没有直接影响;可下次 GC再次回收
问题:没有直接影响;可下次 GC再次回收
漏标
漏标:原本断开 的白色,在扫描期间被黑色引用了
问题:如果白色不变色,就会被清理,被引用的对象反而被回收了
问题:如果白色不变色,就会被清理,被引用的对象反而被回收了
解决方案
原始快照
G1、Shenandoah
当灰色对象要删除对白色的引用时,就做快照;
扫描结束后再次以灰色为根再次扫描一边
当灰色对象要删除对白色的引用时,就做快照;
扫描结束后再次以灰色为根再次扫描一边
增量更新
CMS
如果在扫描期间,黑色对象引用了白色的对象,那么就把黑色变灰色
结束后再次扫描一边
CMS 只能处理 “漏标”,保证对象不丢失,但不能处理浮动垃圾
如果在扫描期间,黑色对象引用了白色的对象,那么就把黑色变灰色
结束后再次扫描一边
CMS 只能处理 “漏标”,保证对象不丢失,但不能处理浮动垃圾
引用
强引用
Object a=new Object()
不会被回收
不会被回收
软引用
SoftReference
如果内存不足,就会回收掉;
回收后还是不足,就会OutofMemoryError
如果内存不足,就会回收掉;
回收后还是不足,就会OutofMemoryError
弱引用
weakReference
无论内存是否充足,GC时都会回收
无论内存是否充足,GC时都会回收
虚引用
用于被引用对象 被GC时会产生一个系统通知
根节点枚举
根节点枚举必须要在JVM 停止运行时进行,类似在一个快照上进行图的搜索
OopMap
为了降低STW的时间提高枚举效率,
HotSpot使用OopMap的数据结构来存放根节点
HotSpot使用OopMap的数据结构来存放根节点
一旦类加载完毕,就会把对象在什么偏移量上有根节点引用记录;
对应线程栈和寄存器里那些位置是引用都会记录
对应线程栈和寄存器里那些位置是引用都会记录
安全点
引用总是不断在变化的,在每个指令后都生成OopMap 既占用内存,又影响效率
JVM在安全点进行OopMap的记录
JVM在安全点进行OopMap的记录
安全点的位置
长时间执行指令的地方:
- 循环
- 方法调用
- 异常调用
线程如何达到安全点,并停止?
抢先式【弃用】
先把所有用户线程中断;如果中断的位置不是安全点,则再次唤醒跑到安全点
主动通知式
让线程不断去轮询标志位 是否需要中断;如果需要就近找安全点中断
HotSpot 巧妙的使用了一个 内存页不可读的指令:
在不需要STW时 某个内存页一直可读;需要时将其设置为不可读
既可以让线程暂停挂起,又实现了轮询;
在不需要STW时 某个内存页一直可读;需要时将其设置为不可读
既可以让线程暂停挂起,又实现了轮询;
安全区域
对于sleep或者blocked的线程不能响应中断信号,怎么办?
记忆集和卡表
当出现夸代引用时,避免把整个老年代加入GCRoots扫描,造成效率底下
分类
字节:具体到某一个内存地址,包含了跨代引用指针
对象:只要对象某一个字段包含跨代引用指针
卡:某一块内存区域含有某些对象有跨代指针
卡表
类似于Map结构;value是一块内存区域,称为卡页
卡页中只要有一个对象字段有跨代引用指针,这个页就称为“脏页”
卡页中只要有一个对象字段有跨代引用指针,这个页就称为“脏页”
如何维护卡表的变化
写屏障“AOP”
在引用对象赋值时,利用写屏障产生类似AOP的环绕效果,对卡表进行更新
每次更新对象引用都会自动更新卡表
每次更新对象引用都会自动更新卡表
-XX:+UseCondCardMark
通过这个参数开启后,可以避免卡表的“伪共享”缓存行问题
每次更新卡表前,先查询表元素,如果没有标记再标记Dirty
每次更新卡表前,先查询表元素,如果没有标记再标记Dirty
回收方法区
废弃的常量
比如常量池中的对象没有任何引用时
有可能会被回收
有可能会被回收
不再使用的类
回收的必要条件:
- 该类的所有实例化对象都都已经回收
- 加载该类的Classloader已经被回收
- 该类的java.lang.Class对象没有任何的引用,无法在任何地方通过反射获取到该类
垃圾回收算法
对象分代假说
弱分代假说:大部分对象 朝生夕灭
强分代假说:熬过越多次GC的对象就越难回收
夸分代假说:跨代引用的对象相对于同代引用 数量极少
夸分代解决方案:
"记忆卡"法
在新生代上建立一个全局的记忆卡(Remmbered Set),
记录存在跨代引用的老年代内存区域,在Minor GC时,
只有包含了跨代引用的小块内存里的对象才会被加入到GCRoots进行扫描
"记忆卡"法
在新生代上建立一个全局的记忆卡(Remmbered Set),
记录存在跨代引用的老年代内存区域,在Minor GC时,
只有包含了跨代引用的小块内存里的对象才会被加入到GCRoots进行扫描
标记-清除算法(Mark-Sweep)
优点:简单粗暴,直接将找出需要回收的对象,回收即可
缺点:
- 效率低,如果有大量对象需要回收就会产生大量的标记清除动作
- 容易产生碎片,回收后的内存区域呈现碎片话的状态,不利于分配大内存
代表算法:CMS
标记-复制算法
将内存分为两块,每次只分配其中一块区域;GC时把存活的对象直接复制到另一块区域;清空已使用区域即可
优点:实现简单,执行效率高,没有内存碎片;适合"朝生夕灭"的对象回收
缺点:部分空间不可使用;
在Seril ParNew中 Eden和Servivor为 8:1:1也就意味着有1/10的内存不可用
在Seril ParNew中 Eden和Servivor为 8:1:1也就意味着有1/10的内存不可用
代表算法:新生代的GC算法 Seril和eParNew
标记-整理算法(Mark-Compact)
在标记清除的基础上,将存活的对象移动到内存区域的一侧,形成空闲的一整块内存区域
优点:内存空间碎片少,长时间看有助于系统吞吐量的提高
缺点:整理是一个耗时操作,会增加GC的时长
代表算法:Parallel Scavenge
垃圾回收器
CMS (Concurrent Mark Sweep)
老年代回收器,搭配ParNew新生代使用
回收过程
1.初始化标记
标记一下GCRoots根节点能直接关联到的对象
2.并发标记
从GCRoots为根开始遍历对象图
3.重新标记
由于在并发标记,在标记过程中,和用户线程同时执行,
所以会有对象引用变化,存在“多标”和“漏标”现象,
需要 三色法的“增量更新” 再次进行标记
所以会有对象引用变化,存在“多标”和“漏标”现象,
需要 三色法的“增量更新” 再次进行标记
4.并发清除
多线程同时回收“白色”的对象
优缺点
优点:低停顿、高并发
缺点
1.对CPU有要求
回收时占用1/4的算力;对低于4核的CPU不友好
回收时占用1/4的算力;对低于4核的CPU不友好
2.无法处理“浮动垃圾”
由于采用了“增量更新”,只能处理“漏标”的情况,
对于“多标”的“浮动垃圾”只能等待第二次回收;
这样垃圾回收不充分,可能造成老年区内存不够用,
进而导致FullGC 的发生
由于采用了“增量更新”,只能处理“漏标”的情况,
对于“多标”的“浮动垃圾”只能等待第二次回收;
这样垃圾回收不充分,可能造成老年区内存不够用,
进而导致FullGC 的发生
3.会产生内存碎片
由于CMS是“标记-清除”垃圾回收器,难免产生内存碎片
不过CMS会根据配置,在FUllGC达到一定次数后,进行依次内存整理
由于CMS是“标记-清除”垃圾回收器,难免产生内存碎片
不过CMS会根据配置,在FUllGC达到一定次数后,进行依次内存整理
G1 (Garbage First)
执行引擎
类加载子系统
0 条评论
下一页