JVM整理
2021-03-23 15:14:29 7 举报
AI智能生成
有问题,欢迎指正
作者其他创作
大纲/内容
垃圾回收机制
判断对象是否存活
1.引用计数法
2.可达性分析
可作为GC Root的对象
虚拟机栈中所引用的对象
方法区中类静态属性引用的对象
方法区中常量池对象引用
本地方法JNI
同步锁(synchornized)持有的对象
如何标记对象死亡
至少需要经历两次标记
1.第一次标记过程
不执行
1.未覆盖finalize方法
2.已经执行过一次
2.第二次标记过程
会将第一次标记的对象放入一个由虚拟机创建的F-GC队列(低调度)中
如何逃脱
1.在finalize方法中重新挂上一个对象引用(font color=\"#c41230\
四种引用类型
1.强引用
2.软引用
可以创建于缓存操作
3.弱引用
每次垃圾回收会清理的对象
4.虚引用
font color=\"#c41230\
垃圾收集
提出理论
弱分代假说---新生代
绝大部分对象都是朝生夕灭
强分代假说---老年代
名词整理
PartialGC (局部GC操作)
Minor GC(新生代GC)
MajorGC(老年代GC)
MIXED GC(混合GC)--只有G1收集器会有这种操作
FULL GC(对整个java堆和方法区的GC操作)
算法整理
1.标记--清除算法(最基础)
缺点
2.标记--复制算法(新生代使用)
3.标记--整理算法(老年代使用)
垃圾收集器
Serial(新生代使用)
SerialOld(老年代使用)
ParNew(新生代使用)
多线程垃圾回收方式
CMS
4.并发清除
Parallel Scavenge(新生代使用)
Parallel Old(老年代使用)
G1(通用)
span style=\"font-size: inherit;\
垃圾收集器并发和并行概念
并行
并发
额外知识:CMS和ParNew收集器新老年代的晋升及回收策略
后端编译与优化
即时编译器
判定热点代码
被多次调用的方法
被多次执行的循环体
热点探测方式
基于采样的热点探测
基于计数器的热点探测
为每个执行方法创建一个计数器
虚拟机类加载机制
class文件结构
“Class文件”并非特指某个存在于具体磁盘中的文件,而应当是一串二进制字节流,无论其以何种形式存在,包括但不限于磁盘文件、网络、数据库、内存或者动态产生等
魔数
版本号
常量池
字面量
符号引用
访问标志
字段表集合
方法表集合
名称索引
描述符索引
属性表集合
类加载生命周期
加载
1.根据全类名获取二进制流数据
2.将这个字节流所代表的静态存储结构转化为方法区的运行时数据结构
3.在font color=\"#c41230\
验证
1.文件格式验证
2.元数据验证
3.字节码验证
4.符号引用验证
准备
为类中定义的变量(即静态变量,被static修饰的变量)分配内存并设置类变量初始值的阶段
是Java虚拟机将常量池内的符号引用替换为直接引用的过程
初始化
初始化的必备6种情况
1.遇到new、getstatic、putstatic或invokestatic这四条字节码指令
new关键字
静态类型字段
调用类型的静态方法
2.反射调用
5.使用动态代理机制
使用
卸载
类加载器(font color=\"#c41230\
启动类加载器
扩展类加载器
jdk9之后变为平台类加载器
应用程序类加载器
自定义类加载器
双亲委托模型
JVM整理
java内存区域
运行时数据区域
1.程序计数器(线程私有)
2.java虚拟机栈(线程私有)--数据结构为栈帧
生命周期与当前线程相同
局部变量表
3.本地方法栈
同2一样也会出现同样异常
4.java堆(所有线程共用数据)
几乎所有的对象创建都是在这里进行分配内存操作
jdk1.7以上将 字符串常量池和class和静态变量放置在了堆中
5.方法区(线程共用)
1.7之前
永久代
1.8之后
元空间
只存储类型信息等描述信息,并且放在了直接内存中
运行时常量池
直接内存
对象访问定位
直接指针访问
句柄访问
对象内存布局
对象头
markword
哈希码
25bit
分代年龄
4bit
锁标志位
2bit
GC标志
1bit
实例数据
对齐填充
填充为8的倍数
内存溢出和泄露以及虚拟机监控工具
内存溢出
产生原因
堆溢出
栈溢出
方法区和运行常量池溢出
本地内存溢出
内存泄漏
原因
长生命的对象持有短生命对象的引用
连接未关闭
变量作用域不合理
内部类持有外部类
hash值改变
jdk工具
jps:虚拟机进程工具
jstat:虚拟机统计信息监视工具
jinfo:Java配置信息工具
jmap:Java内存映像工具
jhat:虚拟机堆转储快照分析工具
jstack:Java堆栈跟踪工具
java线程内存模型
主内存与工作内存
主内存
所有变量都存储在这
工作内存
工作线程无法读取到别的线程的变量副本操作
内存交互操作
lock
unlock
read
load
use
assign(赋值)
store
write
将store操作的变量数据存储到主内存中
原子性
可见性
volatile
有序性
线程内表现为串行的语义
指令重排序
为了解决各个操作之间执行速度不一致
先行发生原则
程序次序规则
传递性
如果操作A先行发生于操作B,操作B先行发生于操作C,那就可以得出操作A先行发生于操作C的结论
管程锁定规则
一个unlock操作先行发生于后面对同一个锁的lock操作
volatile变量规则
对一个volatile变量的写操作先行发生于后面对这个变量的读操作
线程启动规则
线程中断规则
线程终止规则
线程的实现方式(不指java线程)
内核线程实现(1:1)
用户线程实现
混合实现
Java线程实现
“主流”平台上的“主流”商用Java虚拟机的线程模型普遍都被替换为基于操作系统原生线程模型来实现,即采用1:1的线程模型
java线程调度
协同式线程调度
在此多线程系统中,线程的执行时间由线程本身控制,线程把自己的工作执行完之后,要主动通知系统切换到另外一个线程上。
优势
实现简单,由于线程要把自己事情干完才会进行线程切换,切换操作对线程自己是可知的,所以没什么线程同步问题
线程执行时间不可控,如果一个线程编写有问题,一直不告知系统进行线程切换,那么程序就会一直阻塞在那里
抢占式线程调度
在此多线程系统中,每个线程将由系统来分配时间,线程的切换不由线程本身决定
线程执行时间是系统可控的,也不会出现一个线程导致整个进程阻塞的问题。Java使用的线程调度方法就是这个
java线程安全与锁优化
java线程安全
不可变
只要一个不可变的对象被正确地构建出来(即没有发生this引用逃逸的情况),那其外部的可见状态永远都不会改变,永远都不会看到它在多个线程之中处于不一致的状态。“不可变”带来的安全性是最直接、最纯粹的
绝对线程安全(java中的并发类库基本上不是绝对线程安全)
定义其实是很严格的,一个类要达到“不管运行时环境如何,调用者都不需要任何额外的同步措施”可能需要付出非常高昂的,甚至不切实际的代价
相对线程安全
保证对这个对象单次的操作是线程安全的,我们在调用的时候不需要进行额外的保障措施,但是对于一些特定顺序的连续调用,就可能需要在调用端使用额外的同步手段来保证调用的正确性
线程兼容
对象本身并不是线程安全的,但是可以通过在调用端正确地使用同步手段来保证对象在并发环境中可以安全地使用
线程对立(避免使用)
线程安全的实现方法
互斥同步(属于一种悲观的并发策略,其总是认为只要不去做正确的同步措施(例如加锁),那就肯定会出现问题)
synchronized关键字
非公平锁
重入锁(ReentrantLock),ReentrantLock与synchronized相比增加了一些高级功能,主要有以下三项:等待可中断、可实现公平锁及锁可以绑定多个条件
等待可中断
指当持有锁的线程长期不释放锁的时候,正在等待的线程可以选择放弃等待,改为处理其他事情
公平锁
多个线程在等待同一个锁时,必须按照申请锁的时间顺序来依次获得锁;而非公平锁则不保证这一点
锁绑定多个条件
非阻塞同步(乐观策略)
使用CAS策略(简单来说,比较和替换是使用一个期望值和一个变量的当前值进行比较,如果当前变量的值与我们期望的值相等,就使用一个新值替换当前变量的值)
无同步方案
锁优化
是为了在线程之间更高效地共享数据及解决竞争问题,从而提高程序的执行效率
自旋锁
锁消除
一些代码要求同步,但是对被检测到不可能存在共享数据竞争的锁进行消除
锁粗化
虚拟机探测到有这样一串零碎的操作都对同一个对象加锁,将会把加锁同步的范围扩展(粗化)到整个操作序列的外部
轻量级锁(CAS实现锁升级)
如果出现两个线程以上同时抢占,那么会将轻量级锁膨胀为重量级锁
偏向锁
这个锁会偏向于第一个获得它的线程,如果在接下来的执行过程中,该锁一直没有被其他的线程获取,则持有偏向锁的线程将永远不需要再进行同步
当一个对象当前正处于偏向锁状态,又收到需要计算其一致性哈希码请求时,它的偏向状态会被立即撤销,并且锁会膨胀为重量级锁
0 条评论
回复 删除
下一页