JVM
2023-05-25 18:40:35 0 举报
AI智能生成
jvm详解;从java文件到class;垃圾回收
作者其他创作
大纲/内容
类加载子系统
jvm加载机制
过程
编译期
java文件被编译成.class文件,给java进程使用
一个.class包含
常量池
类型信息
字典信息
方法信息
运行期
window系统下,java.exe调用os jvm.dll文件 linux ..
创建引导类加载器BootstrapClassLoader,它负责调用java代码,创建JVM启动器sum.misc.Launcher。启动器负责加载类启动器,extClassLoader
执行main方法,使用AppClassLoader,把所有.class加载到jvm
加载
双委派模型
负责委托机制
当一个ClassLoader加载一个类,除非显示的使用另外一个类加载器,该类依赖及引用的类也由这类加载器加载
使用javap -v class文件,可以查看字节码的内容
读取.class字节码文件,使用时才会加载,比如new 对象,class文件加载到方法区,同事在堆内存生成代表这个类的java.lang.Class文件,作为访问方法区文件各种数据的入口
验证
字节码是有固定的格式
准备
给静态常量分配内容
解析
静态链接
在class文件加载期间,将符号引用替换为直接引用,该阶段会吧一些静态方法替换指向数据所存内存的执行或句柄等
动态链接
在程序运行期间,把符号引用替换成直接引用
初始化
对类的静态变量赋值
执行静态代码块
使用
对象创建过程
类加载检查
分配内存
栈分配
逃逸分析、标量替换
堆分配
内存分配后,变量值可以初始化默认值,保证属性不赋值也可以直接使用
初始化
赋值
设置对象头
Mark Word
Klass
指向方法区、类元信息
数组长度
只有数组类型才存在
执行<init>方法
执行构造函数
实例属性按照代码赋值
卸载
方法区回收内存,但是场景非常少
该类所有实例被回收 堆内存不存在实例
堆内存class被回收
加载该类的ClassLoader被回收
类加载器
引导类加载器
负责加载jvm运行时JRE/lib下核心类库;rt.jar / charsets.jar
扩展包加载器
负责加载jvm运行时JRE/lib下ext扩展目录下jar包
AppClassLoader
负责加载ClassPath目录下的类包
自定义加载器
双委派机制
他是一种设计,使用组合模式实现,当子类找不到class时,去父类寻找,如果没有,继续向父类寻找,以此类推
优势
沙箱安全
防止核心api被篡改
java开头的包名无法通过类加载检查
保证被加载类唯一性
保证类只会加载一次
字节码
结构
魔数
jdk版本
次版本号
主版本号
常量个数
常量池
字面量
符号引用
类描述信息
访问修饰符
类名
父类名
接口数
接口名称
字段描述
字段个数
字段表
保存多个字段的描述信息
访问修饰符
字段名
方法描述
方法个数
方法表
方法访问修饰符
方法名
方法描述
字段表
保存方法内临时变量
字段描述
字段名
字段描述
编译期确定了栈内栈帧的局部变量表栈的深度
局部变量表索引保存this指针
附加信息
总结
JVM执行的是二进制字节码
字节码只描述class文件包含的信息,不会包含父类继承来的属性,除非覆盖了父类接口的方法
方法对应的栈帧,局部变量表和操作数栈,在编译器就可以确认
常量池的池化思想,节省了资源
类可以实现接口数,可以声明变量数量是必须小于65535
字节码执行引擎
性能调优主题
运行时数据区
内存模型
线程私有
本地方法栈
调用native方法
线程栈
一个方法一个栈帧
局部变量表
操作数栈
动态链接
方法出口
栈内对象创建
程序计数器
保存当前线程执行的指令段,用户顺序执行程序段,线程切换时,配合字节码执行引擎保存当前线程上下文状态
线程共享
堆
新生代(1/3)
eden (8/10)
suvivor(2/10)
s0(1/10)
s1(1/10)
一个对象,s0与s1只会用到一个
minor gc /young gc
老年代(2/3)
full gc
速度比minor gc慢十倍
方法区
保存字节码文件
常量
静态变量
类元信息
常量池分类
常量池
class字节码文件中一段字节码片段,用户存放java文件编译后的字面量,符号引用
运行时常量池
class字节码中的常量池,加载到方法区后,就会变成运行时常量池
字符串常量池
保存字符串这种字面量的一块内存区域
源码设计
是一个StringTable,保存了堆内存中字符串是咧对象的引用
设计思想
池化思想
字符串作为最基本的数据类型,为了避免频繁创建,提升性能,设计了字符串常量池
字符串的三种操作
String str = ""
字符串只会在常量池
new String()
常量池,堆内存都有
string.intern
常量池/堆内存
总结
创建字符串对象是,直接用String str = “demo”,少用new String方式可以减少堆内存实例数量,减少内存占用,减轻gc压力
复用字符串对象是,intern和String str = “demo”方法没区别,都是去拿字符串常量池的对象复用
垃圾回收
垃圾收集算法
分带收集理论
根据对象的生命周期,占用内存大小,去分配、使用、管理不同的内存区域,并使用不同的加料机回收算法,去进行内存回收,体现在实现上,使用不同的垃圾去回收不同的内存区域
分代
新生代
多数在线程执行完毕,回收,生命周期短
老年代
长期存活对象
算法分类
标记-复制
效率高、需要额外村妇永福复制存活对象
适用于新生代,及可以将长期存活对象快速复制到老年代,又可以在新生代内部分代,使用eden区与suvivor区,进行复制,增加新生代回收效率
标记-清除
实现简单,就是对象不在被引用,标记删除
会常生内存碎片
适用于老年代,因为老年代没有额外空间可以进行复制算法,老年代保存长期存活对象,也不应该再去进行复制,直接清除或清除后整理内存,避免老年代空间浪费
标记-整理
对不连续性且存活的对象进行内存移动。主要是为了内存碎片设计的算法
专门针对老年代特点,长期存活的对象,防止内存碎片过多导致full gc
解决垮掉引用
什么是跨带引用
minor gc时,老年代引用可能指向新生代对象,如果只是扫码新生代gc roots,那么老年代引用的对象会被误认为垃圾,被回收
记忆集
新生代引入一种记忆集的数据结构,记录从非收集区到收集区的引用,那么minor gc会扫描新生代 + 记忆集gc roots
卡表
记忆集的一种实现方式,在新生代维护一个字节数组,将老年代划分为一个个卡页,用字节数组中的每一位索引代表老年代每一个卡页?
垃圾回收器
Serial
特点
单线程回收
新生代采用 标记复制算法,老年代采用标记-整理算法
用途
Serial作为新生代收集器, Serial-old作为老年代收集器
parallel Scavenge 与Serial old作为jdk1.5之前的收集器配合使用
Serial old作为CMS的后备方案
Parallel Scavenge
特点
Serial收集的多线程版本,默认核数为CPU核数
新生代采用 标记复制算法,老年代采用标记-整理算法
是jdk8默认收集器
用途
提供了很多参数用户配置最合适的停顿时间和吞吐量
ParNew & CMS
ParNew
特点
多线程收集
采用标记-复制算法
用途
作为新生代收集器,与CMS配合工作
CMS
特点
真正开始用户线程与GC线程并行执行的收集器,并行收集器的原始
用户体验好,STW时间变短,但是整体回收时间拉长
采用标记-清除算法
可以压缩整理内存
并行收集会带来额外的浮动垃圾和漏标的问题
浮动垃圾下次回收
漏标使用STAB和增量更新解决
用途
用于用户停顿时间敏感、堆内存4-8G情况
算法底层实现
三色标记
STAB
增量更新
读屏障
写屏障
G1
核心思想
围绕用户停顿时间,进行C++底层实现与参数暴露
特点
用户停顿时间短
将堆内存默认划分2048个Region
大对象不在进入老年代,而是进入特有的大对象Region
局部采用标记-复制算法,从整体上看又是标记-整理算法
垃圾回收阶段会根据设置的最大停顿时间,对回收Region排序优先级,优先回收性价比高的Region
用途
适合于8G以上的大内存,多核处理器,停顿时间可控
垃圾收集分类
young gc
Eden初始化为堆内存5%,Eden沾满,如果G1计算回收时间小于期望停顿时间,在发生young gc,直接扩容年轻代
mixed gc
full gc
STW,使用单线程进行标记、整理、压缩内存空间,好空间出一批Region给mixed GC
ZGC
核心思想
低延迟
特点
内存布局
小型Region
中型Region
大型Region
支付NUMA架构
用途
TB级别的堆,最大GC停顿时间不超过10MS
如何选择垃圾回收器
优先调整堆内存大小,让JVM自行选择
硬件差:内存小,cpu核数少
串行或者jvm自行选择
要求GC停顿时间少
并行收集器
4G一下parallel
4-8G ParNew + CMS
8G以上G1
几百G使用ZGC
安全点与安全区域
JVM的么给线程代码执行到一个点或者区域时,对象引用状态不在发生变化,这时可以GC,这些代码或者代码区域就算安全点与安全区域
安全点
方法返回之前
调用某个方法之后
抛出异常的位置
循环末尾
安全区域
字节码执行引擎
解释、执行字节码文件,产生系统调用,讲jvm执行翻译为CPU指令
调优工具
jdk工具
jps
列出所有java进程
jmap
展示JVM进程内存信息,实例个数等
jstack
打印线程栈信息
jinfo
查看jvm进程配置信息
jstat
统计JVM gc信息、类加载信息、jit信息
理论
jvm调优规则
默认参数能不调就不调
如果非要调整,尽量短期存活的对象都在新生代,可以增加新生代内存,同时需要避免3个因素带来的full gc
大对象直接进来老年代
动态年龄判断机制
老年代空间分配担保机制
jvm估算运行内存的方法
年轻代
年轻代对象增长的速率
young gc的触发频率和每次耗时
每次young gc 有多少对象存活和进入老年代
老年底
full gc触发频率和每次耗时
如何根据gc情况定位到源码的位置
全局搜索
内存占用过多,代码片段执行数也可以很多,可以查看占用cpu比较高的线程代码
内存模型四步走
建立内存分布模型
计算gc对象实例流转情况
结合业务系统,推测可能导致频繁full gc的4个问题
新生代对象年龄满足阈值,移动老年代
大对象
动态年龄判断
老年代空间分配担保机制
0 条评论
下一页