jvm的知识体系
2022-03-09 08:40:58 0 举报
AI智能生成
JVM
作者其他创作
大纲/内容
类加载机制
图解
装载
1、通过一个类的全限定名获取定义此类的二进制字节流
2、将字节流所代表的的静态存储结构转化为方法区的运行时数据结构
3、在java堆中生成了一个代表这个类的java.lang.Class对象,作为对方法区中这些数据的访问入口
链接
1、验证:保证被加载类的正确性
文件格式验证/元数据验证/字节码验证/符号引用验证
2、准备
为类的静态变量分配内存,并将其初始化为默认值
3、解析
把类中的符号引用转换为直接引用
初始化
对类的静态变量,静态代码块执行初始化操作
类加载器
图解
双亲委派原则
定义
如果一个类加载器在接到加载类的请求时,它首先不会自己尝试加载这个类,而是把这个请求任务委托给父类加载器去完成,一次递归,如果父类加载器可以完成加载任务,就成功返回;只有父类加载器无法完成此加载任务时,才自己去加载。
优势
java类随着加载它的类加载器一起具备了一种带有优先级的层次关系。
例如:java中的Object类,它存放在rt.jar之中,无论哪一个类加载器要加载这个类,最终都是委派给处于模型最顶端的启动类加载器进行加载,因此object类在各种类加载环境中都是同一个类。如果不采用双亲委派模型,那么由各个类加载器自己取加载的话,那么系统中会存在多种不同的Object类
垃圾回收
如何判断一个对象是垃圾
引用计数法
缺点:循环引用无法辨别是垃圾,一旦相互持有引用,就导致对象永远无法被回收
可达性分析
由GCroot出发,开始寻找,看看某个对象是否可达
GC Root:类加载器、Thread、本地变量表、static成员、常用引用、本地方法栈中的变量
垃圾回收算法
标记清除
子主题
复制
标记整理
子主题
分代收集算法
young区:复制算法
old区:标记清除或标记整理
垃圾收集器
1、Serial
单线程的收集、适用于新生代、暂停用户代码
2、Serial Old
子主题<br>
3、ParNew
多线程收集、复制算法、 适用于新生代
4、Parallel
相对于ParNew,更加关注于吞吐量
5、Parallel Old
标记整理的算法
6、CMS
初始标记非常快所以用单线程
7、G1
子主题
子主题
jdk11 增加一个新的 :ZGC
垃圾收集器分类
1、串行收集器
Serial
Serial Old
只能有一个垃圾回收线程执行,用户线程暂停。
适用于内存比较小的嵌入式设备
2、并行收集器
Parallel Scanvenge
Parallel Old
多条垃圾收集线程并行工作,但此时用户线程仍然处于等待状态。
适用于科学计算、后台处理等交互场景
3、并发收集器
CMS
G1
用户线程和垃圾收集线程同时执行(但并不一定是并行的,可能是交替执行的),垃圾收集器在执行的时候不会停顿用户线程的运行。
适用于相对时间有要求的场景,比如web
吞吐量和停顿时间
停顿时间->垃圾收集器进行垃圾回收终端应用执行响应的时间
停顿时间越短越适合需要和用户交互的程序,良好的响应速度能提升用户体验
吞吐量->运行用户代码时间/(运行用户代码时间+垃圾收集时间)
高吞吐量则可以高效地利用CPU时间,尽快完成程序的运算任务,主要适合在后台运算而不需要太多交互的任务
两个指标也是评价垃圾回收器好处的标准,其实调优也就是在观察者两个变量。
如何选择合适的垃圾收集器
优先调整堆的大小让服务器自己来选择
如果内存小于100M,使用串行收集器
如果是单核,并且没有停顿时间要求,使用串行或JVM自己选
如果允许时间停顿超过1秒,选择并行或JVM自己选
如果响应时间最重要,并且不能超过1秒,使用并发收集器
性能优化
OOM
内存溢出
用mat工具处理
GC优化
打印GC日志文件<br>
分析工具
gceasy.io
关注点
吞吐量
停顿时间
追求高吞吐量低停顿
性能优化 指南
发现问题
GC频繁
死锁
OOM
线程池不够用
CPU负载过高
排查问题
打印出GC日志,查看minor gc/major gc,结合工具gc viewer/gceasy.io
jstack查看线程堆栈信息
dump出堆文件,使用MAT或者其他工具分析
合理使用jdk自带的jconsole,jvisualvm,阿里的arthas等实时查看JVM状态
灵活应用jps,jinfo,jstat,jmap等常用命令
解决方案
适当增加堆内存大小/选择合适的垃圾收集器
使用zk,redis实现分布式锁
设置本地,Nginx等缓存减少对后端服务器的访问
后端代码优化及时释放资源/合理设置线程池中的参数
集群部署从而减少单节点的压力
利用一些消息中间件比如MQ,Kafka实现异步消息
运行时数据区
堆
所有的对象都在堆里面进行分配
堆是java虚拟机所管理内存中最大的一块
起始:在虚拟机启动时创建,被所有线程共享
java对象实例以及数组都在堆上分配
当堆无法满足内存分配需求时,将抛出OutOfMemoryError
方法区
方法区是各个线程共享的内存区域,在虚拟机启动时创建
存储内容:被虚拟机加载的类信息、常量、静态变量、即时编译器编译后的代码等数据
方法区记录的是方法以外的一些数据
虚拟机规范把方法区描述为堆的一个逻辑部分,但是它却有一个别名叫做Non-Heap(非堆),目的是和java堆区分开来
异常产生:方法区无法满足内存分配需求时,抛出OutOfMemoryError异常
jdk的版本区分
jdk8:Metaspace【元空间】
jdk6或7中就是Perm Space【永久代】
Run-Time Constant Pool 在方法区分配
图解
jvm
java虚拟机栈
是什么?
虚拟机栈是一个线程执行的区域,保存着线程中方法的调用状态。
一个java线程的运行状态,由一个虚拟机栈来保存
虚拟机栈肯定是线程私有,独有的,随着线程的创建而创建
栈帧
每个线程执行的方法,为该栈的栈帧,即每个方法的执行对应一个栈帧
细节
每个栈帧对应一个被调用的方法,可以理解为一个方法的运行空间
每个栈帧中包含
局部变量表
操作数栈
图解
子主题
子主题
动态链接
程序运行当中会动态调用某些类的方法
方法返回地址
上一个方法执行完,返回到指定行数
附加信息
调用一个方法,就会向栈中压入一个栈帧;一个方法调用完成,就会把该栈帧从栈中弹出
程序计数器
当前线程所执行方法的位置,在虚拟机栈中 The PC register<br>
一个jvm进程中有多个线程在执行,而线程中的内容是否能够拥有执行权,是根据cpu调度来的。<br>假如线程A正在执行到某个地方,突然失去了CPU的执行权,切换到了线程B了,然后当xianchengA再获得Cpu执行权的时候,怎么能继续执行呢?这就是需要在线程中维护一个变量,记录线程执行到的位置
程序计数器的说明
1、程序计数器占用的内存空间很小,由于java虚拟机的多线程是通过线程轮换切换,并分配处理器执行时间来实现的,任意时刻,一个处理器只会执行一个独立的程序计数器(线程私有)
2、如果线程正在执行Java方法,则计数器记录的是正在执行的虚拟机字节码指令的地址
3、如果正在执行的是Navicat方法,则这个计数器为空
线程私有
本地方法栈
图解
jvm内存模型
图解
子主题
子主题
young区
young区分为两大块,一个是Survivor区(s0+s1),一块是Eden区。 Eden:S0:S1 = 8:1:1
为何如此分配?
因为Eden是负责存放新生对象的区域,大部分的对象都是朝生夕死的因此必然会占用大量的Eden区域,而如果变小的话会造成Eden不够使用的情况。Survivor区的存在是为了在GC过程中活下来的作为存留,也是为了避免产生碎片化的空间
Eden
如果没有Eden和survivor区会导致GC后出现空间碎片化,导致空间不连续会造成大的对象可能会过早的分配不下导致过早的GC
正常对象创建所在区域,大多数对象“朝生夕死”
Survivor
Survivor区分为S0和S1,也可以叫From和To
同一时间点上,S0和S1只能有一个区有数据,另外一个是空的,也是为了处理空间碎片化的问题
old
一般old区都是年龄比较大的对象,或者相对超过了某个阈值的对象
Survivor区如果分配不下了会向old区借空间
工具
子主题<br>
命令 jvisualvm
子主题
JVM工具
JVM参数
标准参数
-x参数
-XX参数
-XX:[+/-]
-XX:+UseG1GC
-XX:<name>=<value>
-XX:InitialHeapSize = 100M
其他参数
-Xms100M ==》 -XX:InitialHeapSize=100M
-Xmx100M
-Xss100
JVM命令
jps
可以查看当前的java进程
jinfo
可以查看某个java进程目前参数设置的情况
示例
示例2
示例1
jstat
查看java进程的统计信息
示例
查看当前进程类加载的统计情况 10秒 打印十次
查看当前进程的GC情况
jstack
查看当前java进程的堆栈信息
可以用于查看死锁的情况排查
jmap
打印出堆转存储快照
jmap -heap PID
获取堆内存信息
排查内存泄露
jmap -dump:format=b,file=heap.hprof PID
子主题
idea
常用工具
jconsole
jvisualvm
监听某个java进程
arthas
阿里的一个工具
mat/perfma
内存相关信息
gceasy.io/gcviewer
收藏
收藏
0 条评论
下一页