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