JVM
2021-02-25 18:20:17 6 举报
AI智能生成
java虚拟机学习总结
作者其他创作
大纲/内容
实战
Java 堆溢出
虚拟机栈溢出
本地方法栈溢出
方法区溢出
常量池溢出
本机直接内存溢出
垃圾回收
对象回收的判断
<font color="#c41230">1.引用计数算法</font>
<font color="#0076b3">给对象添加一个引用计数器,<br> 每一个地方引用它,计数器值就加1,<br>对象引用失效的时候,计数器就减1.<br>计数器值为0的时候,此对象为垃圾</font>
优点:判定效率高
缺点:对象间循环依赖,此算法
无法正确判断是否为垃圾对象
主流虚拟机没有这种算法
<font color="#c41230">2.可达性分析算法</font>
<font color="#0076b3">GC Roots"的对象作为起始点<br>从这些节点开始向下搜索,<br>搜索所走过的路径称为引用链(Reference Chain)<br>当一个对象到GC Roots没有任何引用链相连时,<br>则证明此对象是不可达的</font>
作为<font color="#c41230">GC Roots对象</font>
<font color="#c41230">栈帧中本地变量表中</font><font color="#0076b3">引用的对象</font>
<font color="#c41230">方法区类静态属性</font><font color="#0076b3">引用的对象</font>
<font color="#c41230">方法区中常量</font><font color="#0076b3">引用的对象</font>
<font color="#c41230">方法栈JNI</font><font color="#0076b3">引用的对象</font>
强引用 Object obj = new Object
软引用 有用非必须
弱引用 非必须
虚引用 最弱的引用
枚举根节点
安全点
安全区域
方法区回收
垃圾回收效率较低
回收内容
废弃常量
<font color="#c41230">无用类</font>
<font color="#c41230">1.该类所有实例都被回收</font>
<font color="#c41230">2.加载该类的ClassLoader被回收</font>
<font color="#c41230">3.该类对应的class对象没有被引用,无法在任何地方反射访问该类方法</font>
垃圾收集算法
<font color="#c41230">标记清除算法(最基础算法)</font>
1.首先标记出所有需要回收的对象
2.在标记完成后统一回收掉所有被标记的对象
缺点1:标记和清除过程的效率都不高
<font color="#c41230">缺点2:产生大量不连续的内存碎片</font>
<font color="#c41230">复制算法</font>
1.内存按容量划分为大小相等的两块
2.一块内存用完了,将存活着的对象复制到另外一块
3.将都是垃圾对象的那块清空内存
缺点1:在对象存活率较高时,复制操作次数多,效率降低
<font color="#c41230">缺点2:內存缩小了一半;需要額外空间做分配担保</font>
<font color="#c41230">标记整理算法</font>
1.标记步骤和标记清除算法相同
<font color="#c41230">2.标记完后垃圾对象移动到一端,然后在清理掉</font>
<font color="#c41230">分代收集算法</font>
<font color="#c41230">新生代</font>
<font color="#c41230">复制算法</font>
<font color="#c41230">老年代</font>
<font color="#c41230">标记-清理或者标记-整理</font>
垃圾收集器
Serial(最基本的收集器)
单线程
垃圾收集的时候,暂停所有线程
优点简单高效,缺点停顿时间长
年轻代-复制算法,老年代-标记整理算法
ParNew收集器
Serial的多线程版本
Parallel Scavenge收集器
新生代收集器
复制算法
吞吐量较高
多线程
Serial Old收集器
Serial的老年代版本
Parallel Old收集器
Parallel的老年代版本
CMS收集器
回收停顿时间短
多线程
标记-清除
1.初始标记 <font color="#c41230">STW</font>
2.并发标记
3.重新标记 <font color="#c41230">STW</font>
4.并发清除
<font color="#c41230">G1收集器</font>
<font color="#c41230">并行并发</font>
<font color="#c41230">分代收集</font>
<font color="#c41230">空间整合</font>
<font color="#c41230">可预测停顿</font>
GC日志
GC日志最前面时间是虚拟机启动以来经过的秒数
【GC 【Full GC 垃圾收集的停顿类型
类加载机制
什么是类的加载机制
类加载的生命周期
<font color="#c41230">加载</font>
<font color="#0076b3">1.通过</font><font color="#c41230">类的全限定名称</font><font color="#0076b3">获取此类的二进制流</font>
<font color="#0076b3">2.将这个二进制字节流所代表的</font><font color="#c41230">静态存储结构转化为方法区的运行时数据结构</font>
<font color="#0076b3">3.内存中生成一个</font><font color="#c41230">java.lang.class对象</font><font color="#0076b3">,作为方法区这个类中各种数据的访问入口</font>
<font color="#0076b3">4.可以从ZIP包,网络,动态代理,其他文件比如JSP,数据库等方式</font>
<font color="#0076b3">5.加载和连接是交叉进行的</font>
<font color="#c41230">连接</font>
<font color="#c41230">验证</font>
确保Class文件的字节流中包含的信息符合当前虚拟机要求
文件格式验证
元数据验证
字节码验证
符号引用验证
<font color="#c41230">准备</font>
为类变量<font color="#c41230">分配内存并</font>设置类变量初始值
<font color="#c41230">解析</font>
1.类或接口的解析
2.字段解析
3.类方法解析
4.接口方法解析
<font color="#c41230">初始化</font>
执行构造器 <clinit>
收集类中所有类变量的赋值动作和静态语句块中的合并语句
子类的clinit<clinit>()执行之前,父类clinit<clinit>()一定执行完成</clinit></clinit>
<clinit>不是必须的,类或者接口中没有对类变量赋值,编译器就可以不生成这个方法
不会执行父接口的<clinit>(),除非要使用父接口中定义的变量</clinit>
<font color="#c41230">多线程环境下可以正常加锁,同步</font>,只有一个会去执行,<clinit>其余挂起,如果有一个线程耗时,那么造成多个线程阻塞</clinit>
使用
卸载
类加载器
<font color="#c41230">双亲委派模型</font>
启动类加载器<font color="#c41230">(虚拟机中的一部分)</font>
将<font color="#c41230">虚拟机识别的类库加载到内存</font>
其他的类加载器<font color="#c41230">(独立于虚拟机外部)</font>
扩展类加载器
应用程序类加载器
负责加载ClassPath上指定的类库
自定义类加载器
工作过程
<font color="#c41230">1. 防止重复加载同一个.class, 保证数据安全</font>
<font color="#c41230">2. 保证核心.class不能被篡改, 保证了Class执行安全</font>
虚拟机字节码执行引擎
运行时栈帧结构
局部变量表
操作数栈
动态连接
方法返回地址
分派
静态分派
运行时数据区域
<font color="#c41230">程序计数器PCR</font>
1.较小的内存
<font color="#c41230">2.当前线程执行的字节码行号指示器</font>
3.字节码指示器:通过改变这个计数器的值来选取下一条需要执行的指令
<font color="#c41230">4.线程私有</font>
5.如果执行的是java方法,则记录的是字节码指令的地址,如果是Native方法,计数器值为空
<font color="#c41230">6.不会内存溢出</font>
java虚拟机栈
<font color="#c41230">1.线程私有</font>
<font color="#c41230">2.生命周期和线程相同</font>
<font color="#c41230">3.每个方法创建</font><font color="#0076b3">栈帧</font>
<font color="#c41230">1.局部变量表</font>
A.存放编译器可知的各种<font color="#c41230">基本数据类型</font>,<font color="#c41230">对象引用</font>
B.64位的long,dubbo占用两个Slot
C.所需的内存在编译器就确定
<font color="#c41230">2.操作数栈(参数传递池)</font>
算术运算的时候是通过操作数栈来进行的
<font color="#c41230">调用其他方法</font><font color="#0076b3">的时候是通过操作数栈来</font><font color="#c41230">进行参数传递</font><font color="#0076b3">的</font>
<font color="#c41230">3.动态链接(运行中方法的符号引用的实际指向地址)</font>
保存当前运行方法<font color="#c41230">符号引用的实际指向地址</font>, 指向常量池的类方法地址
解析
1. Class文件在编译过程中,一切方法调用在Class文件里面存储的都只是符号引用
2. 在class文件的加载过程中, 会把符号引用转换为直接引用(方法在实际运行时内存布局中的入口地址)
<font color="#0076b3">以上为解析调用的过程, 是静态的, 即在类加载时便清楚对象调用方法的实际指向地址</font>
分派
1. 静态分派--场景:重载--编译过程中根据方法的参数类型确定使用哪个方法的符号引用
2. 动态分派--场景:重写--运行过程中根据对象的实际类型选择使用父类/子类中定义的方法的类方法 <br>
<font color="#c41230">4.返回地址</font>
5.异常情况
A:线程请求的栈深度大于虚拟机允许的深度
B:栈拓展内存不够用,内存溢出
本地方法栈
1.线程私有
2.执行的是native方法
<font color="#c41230">3.异常情况和虚拟机栈相同</font>
Java 堆
1.jvm中内存最大的一块
<font color="#c41230">2.线程共享</font>
<font color="#c41230">3.存放对象实例</font>
<font color="#c41230">4.垃圾回收的主要区域</font>
<font color="#c41230">5.新生代</font>
1.Eden
2.From Survivor
3.To Survivor
<font color="#c41230">6.老年代</font>
<font color="#c41230">7.多个线程私有的分配缓存区TLAB</font>
<font color="#c41230">8.物理上不连续的内存空间</font>
9.异常:内存溢出
(永久代)方法区
1.线程共享
<font color="#c41230">2.存储虚拟机加载的类信息,常量,静态变量,即时编译器编译后的代码</font>
<font color="#c41230">3.运行时常量池(存储编译器生成的字面量和符号引用)</font>
直接内存
<font color="#c41230">NIO中使用这块内存居多</font>(例如: netty零拷贝)
非堆内存受总内存大小限制
对象的创建
1.new 指令创建对象
A:检查这个指令的参数能否在<font color="#c41230">常量池中找到类的符号引用</font>
B:检查这个符号引用是否已经被<font color="#c41230">加载,解析,初始化</font>过
C:没有的话先进行类加载过程
<font color="#c41230">D:类加载完成后,分配内存</font>
2.分配内存方式
<font color="#c41230">A:指针碰撞</font>
假设内存规整,指针一边是空闲空间一边是被分配空间,通过指针移动表示使用了多少内存
<font color="#c41230">B:空闲列表</font>
C:两种方式取决于垃圾收集器类型,带有压缩整理的垃圾收集器可以使用指针碰撞
<font color="#c41230">D:并发情况下线程不安全</font>
<font color="#c41230">同步</font>
<font color="#c41230">按照线程划分在不同的空间TLAB</font>
E:内存分配好的话将内存空间初始化为0,然后将hashCode,分代年龄等信息保存在对象头
3.对象的内存布局
<font color="#c41230">A:对象头</font>
第一部分存储<font color="#c41230">hashCode,分代年龄,锁信息</font>等数据(MarkWord)
<font color="#c41230">第二部分类型指针(通过这个指针确定是哪个类的实例)</font>
数组的话还存储数据的长度
<font color="#c41230">B:实例数据</font>
类的字段信息,包括父类的字段信息等
存储顺序受分配策略的影响:相同宽度的字段总是被分配到一起
父类中的字段信息在子类之前
<font color="#c41230">C:对齐填充</font>
虚拟机要求对象大小是8字节的整数倍
如果对象大小不是8字节的整数倍的话,需要通过占位符补全
4.对象的访问定位
<font color="#c41230">句柄访问</font> 详看对象访问句柄图 优点:对象移动reference不用修改,句柄池更新即可
<font color="#c41230">直接指针访问</font> 详细看对象指针图 优点:速度块
假设内粗不规整,占用内存后的对象会记录在一张列表上
内存分配策略
年轻代
<font color="#c41230">对象主要分配在新生代的Eden区域</font>
<font color="rgba(0, 0, 0, 0)">启动本地线程分配缓存的话,则优先在TLAB上分配</font>
优先将对象分配在新生代上, <font color="#c41230">Eden区域内存不够时发送Minor GC</font>
老年代
<font color="#c41230">大对象直接进入老年代</font>
可以配置参数指定大于多大对象直接进入老年代,防止年轻代内存复制频繁
<font color="#c41230">长期存活的对象进入老年代</font>
1.虚拟机给对象添加年龄计数器
2.动态年龄判断
3.空间分配担保
1.Minor GC之前, <font color="#c41230">JVM会判断老年代最大可用的内存是否大于年轻代所有对象内存总和</font>,条件成立那么认为是安全的
2.如果不成立,尝试进行一次Minor GC,这个是有风险的,因为老年代剩余的内存可能不够
3.老年代会判断<font color="#c41230">历史晋升到老年代对象的平均值是否大于老年代剩余的内存</font>, 大于则需要<font color="#c41230">FULL GC</font> 来腾出更多的内存
常用的工具
jps 显示指定系统内所有的虚拟机进程
jstat 监视虚拟机各种运行状态的信息
jinfo 实时查看,调整虚拟机参数
jmap 生成堆转储快照
jhat 虚拟机堆转储快照分析工具
jstack 生成虚拟机当前时刻的线程快照
类文件结构
class文件是一组以8字节为基础单位的二进制流
由无符号数和表构成
0 条评论
下一页