什么是JVM?
定义:Java Virtual Machine - java 程序的运行环境(java二进制字节码的运行环境)
好处:
- 一次编写,到处运行
- 自动内存管理,垃圾回收功能(和当时的c,c++比较)
- 数组下标越界检查(避免改变其他数据)
- 多态(区方法表机制)
相关概念:[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-JZudu324-1649992893009)(jvm笔记.assets/image-20220407220837142.png)]
学习jvm好处
常见jvm
jvm结构
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-dkRpGi7Z-1649992893010)(jvm笔记.assets/image-20220407222045339.png)]
内存结构
1.程序计数器
2.虚拟机栈
3.本地方法栈
4.堆
5.方法区
P5程序计数器:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Iy2YEOol-1649992893010)(jvm笔记.assets/image-20220407222653586.png)]
1.1定义:Program Counter Register 程序计数器(寄存器)
- 作用,是记住下一条jvm指令的执行地址
- 特点
- 是线程私有的
- 不会存在内存溢出
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-iMS15SCy-1649992893011)(jvm笔记.assets/image-20220408105600204.png)]
p7栈
栈-线程运行需要的内存空间
栈帧:每个方法运行时需要的内存(参数,局部变量,返回地址),对应一次方法的调用,
p8栈的演示
Java Virtual Machine Stacks(Java 虚拟机栈)
- 每个线程运行时所需要的内存,成为虚拟机栈
- 每个栈由多个栈帧(Frame)组成,对应着每次方法调用时所占用的内存
- 每个线程只能有一个活动栈帧,对应着当前正在执行的那个方法
p9栈-问题辨析
1.垃圾回收是否涉及栈内存?
每次方法调用后,都会自动弹出栈帧,所以垃圾回收不会涉及栈内存
2.栈内存的分配越大越好吗?
栈内存数越大,线程数越小,只能增加方法递归调用数,而不能增加效率,使用系统默认的栈内存就可以了
3.方法内的局部变量是否是线程安全?
- 如果方法内局部变量没有逃离方法的作用范围,他是线程安全的
- 如果是局部变量引用了对象,并逃离方法的作用范围,需要考虑线程安全
p12栈内存溢出
栈帧过多导致栈内存溢出
栈帧过大导致栈内存溢出
p13 栈内存溢出
json循环引用导致内存溢出
@JsonIgnore
p14线程诊断
top命令查询进程对cpu的占用
top
ps H -eo pid,tid,%cpu
ps H -eo pid,tid,%cpu | grep 32655 //过滤线程
jstack 32655 //列出进程内所有的java线程
//把32655转换成16进制找到线程
p15 jstack查询死锁
p16本地方法栈
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-PPs4Bc5k-1649992893011)(jvm笔记.assets/image-20220409101808007.png)]
调用本地代码时提供的栈
p17堆
之前的栈或者本地方法栈都是线程私有的,堆可以看成线程共享的
Heap 堆
- 通过new关键字,创建对象都会使用堆内存
特点
-
它是线程共享的,堆中对象都需要考虑线程安全的问题
-
有垃圾回收机制
p18 堆内存溢出
方法 一直没有被回收导致的
p19堆内存诊断
1.jps工具
- 查看当前系统中有哪些java进程
2.jmap工具
-
查看堆内存占用情况
jmap -heap 18756
3.jconsole工具
- 图像界面的,多功能的监测工具,可以连续监测
22 方法区
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-FMbHUTJ0-1649992893011)(jvm笔记.assets/image-20220409114754889.png)]
定义:jvm规范对方法区的定义
存储类的数据
23-24 方法区内存溢出
1.8以前 PermGen 永久代
1.8以后 变成一个 Metaspace 元空间 用于保存,所以不会看到内存溢出
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-G6Unaaam-1649992893012)(jvm笔记.assets/image-20220409131025757.png)]
场景
- spring
- mybatis
cglib动态代理
5.4运行时常量池(25-)
-
常量池 Constant pool,就是一张表,虚拟机指令根据这张常量表找到要执行的类名,方法名,参数类型,字面量等信息
-
运行时常量池,是*.class文件中的,当该类被加载,它的常量池信息就会放入运行时常量池(在内存中),并把里面的符合地址变为真实地址
5.5 StringTable(27-)
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-p8Mzxno3-1649992893013)(jvm笔记.assets/image-20220409134423194.png)]
//StringTable["a","b","ab"] hashtable结构,不能扩容
public class Demo1_22{
// 常量池中的信息,都会被加载到运行时常量池中,这时 a, b, ab 都是常量池中的符号, 还没有变为java字符串对象
//ldc #2 会把 a 符号变为 "a" 字符串对象
//ldc #3 会把 b 符号变为 "b" 字符串对象
//ldc #4 会把 ab 符号变为 "ab" 字符串对象
public static void main(String[] args) {
String s1 = "a";
String s2 = "b";
String s3 = "ab";
String s4 = s1 + s2;//new StringBuilder().append("a").append("b").toString() new String("ab")
String s5 = "a" + "b";//到常量池里找"ab",javac在编译期间的优化,结构已经在编译期缺点为ab,s4在编译期不能确定,只有遇到没遇到的对象才放入串池
String s6 = s4.intern();
sout( s3 == s4);//false
sout( s3 == s5);//true
sout( s3 == s6);//true
}
}
5.5 StringTable特性(32-)
- 常量池中的字符串仅是符号,第一次用到时才变为对象
- 利用串池的机制,避免重复穿甲字符串对象
- 字符串变量拼接的原理是StringBuilder
- 字符串常量拼接的原理是编译期优化
- 可以使用intern方法,主动将传尺中还没有的字符串对象放入串池
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Fvh7RD4A-1649992893013)(jvm笔记.assets/image-20220409162811852.png)]
5.6 StringTable位置
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-C7i9OEFe-1649992893013)(jvm笔记.assets/image-20220409164313159.png)]
增加了垃圾回收的效率
5.7 StringTable 垃圾回收
5.8 StringTable 性能调优(39-)
如果,StringTable过长,则查询时间会很长
- 调整- XX:StringTableSize=桶个数
- 考虑将字符串对象是否入池
优点:大量字符串时,可以减少内存占用
6.直接内存(41-)
Direct Memory
- 常见于NIO操作时,用于数据缓冲区
- 分配回收成本较高,但读写性能高
- 不受JVM内存回收管理
java不能直接访问的情况(不使用直接内存):
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-DK1TxIBu-1649992893013)(jvm笔记.assets/image-20220410104117879.png)]
java调用了本地方法(由操作系统提供),cpu从用户态切换为内核态
java代码可以访问的情况(直接内存):减少了一个缓冲区,提高了效率
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-h84XVVZI-1649992893013)(jvm笔记.assets/image-20220410105549688.png)]
6.3分配和回收逻辑
- 使用了Unsafe对象完成直接内存的分配回收,并且回收需要主动调用freeMemory方法
- ByteBuffer的实现类内部,使用了Cleaner(虚引用)来检测ByteBuffer对象,一旦ByteBuffer对象被垃圾回收,那么就会由ReferenceHandler线程通过Cleaner的clean方法调用freeMemory来释放直接内存
垃圾回收(48-)
1如何判断对象可以垃圾回收
1.1引用计数法(49)
被引用数量达到0时,没人引用了,被垃圾回收(python用)
弊端:循环引用,各自的引用计数都是1,还不能被回收
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-zeoPQVwn-1649992893014)(jvm笔记.assets/image-20220410112856320.png)]
1.2可达分析算法(50-)
根对象:不能被当成垃圾回收的对象
如果一个对象被根对象直接简接引用,不能被当做垃圾;如果一个对象没有被根对象直接简接引用,可以被当做垃圾
- Java 虚拟机中的垃圾回收器采用可达性分析来探索所有存活的对象
- 扫描堆中的对象,看是否能够沿用Gc Root对象 为起点的引用链找到该对象,找不到,表示可以引用,找不到,表示可以回收
- 哪些对象可以作为GC Root?
1.3四种引用
1.强引用 没有GC Roots 直接间接引用,可以被垃圾回收
Object object = new Object();
String str = "StrongReference";
2.软引用
- 仅有软引用引用该对象时,在垃圾回收后,内存仍不足时会再次出发垃圾回收,回收软引用对象
- 可以配合引用队列来释放软引用自身
3.弱引用 没有被直接的强引用 ,引用,可以被垃圾回收
- 仅有弱引用引用该对象时,在垃圾回收时,无论内存是否充足,都会回收弱引用对象
- 可以配合引用队列来释放弱引用自身
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-xOPy3hMF-1649992893015)(jvm笔记.assets/image-20220410130316099.png)]
4.虚引用Cleaner
配合ByteBuffer,当ByteBuffer被回收时,进入引用队列,调用Unsafe.freeMemory 释放直接内存
5.终结器引用
当没有对象引用终结器finalize()时,就会创建一个终结器引用。
将终结器引用放入终结器引用,finalizeHandler查看引用队列,发现终结器引用,调用finalize(),之后就可以被回收了。处理优先级较低,不推荐使用这个方法处理资源
2垃圾回收算法(57-)
2.1标记、清除
先标记,把地址放入空闲地址列表,下次分配的时候,去空闲地址列表找。速度快
缺点:容易产生内存碎片
2.2标记、整理
优点:避免过多碎片
缺点:速度慢
2.3复制
把From区域存活的对象,复制到TO区域,清理掉From区域,交换From和To区域
缺点:占用双倍内存空间
3分代垃圾回收(61-)
理解:老年代:家里存放的没有的物品
新生代:楼下垃圾桶里的垃圾
清理流程:
- 对象首先分配在伊甸园区域
- 新生代空间不足时,触发 minor gc,伊甸园和from存活的对象使用copy复制到to中,存活的对象年龄加1并且交换from 、to
- minor gc 会引发 stop the world, 暂停其他用户的线程,等垃圾回收结束,用户线程才恢复运行
- 当对象寿命超过阈值时,会晋升至老年代,最大寿命是15(4bit)
- 当老年代空间不足,会先触发 minor gc,如果之后仍空间不足,那么触发 full gc,STW的时间更长
相关虚拟机参数
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-0qqc0pHk-1649992893015)(jvm笔记.assets/image-20220410154550145.png)]
大对象晋升老年代,不会触发垃圾回收
4垃圾回收器
1.串行
- 单线程
- 堆内存较小,适合个人电脑
2.吞吐量优先
- 多线程
- 堆线程,多核cpu
- 单位时间内,STW的时间最短 0.2 0.2 =0.4
3.响应时间优先
- 多线程
- 堆线程,多核cpu
- 尽可能让单次STW的时间最短 0.1 * 5 = 0.5
4.1吞吐量优先
4.2响应时间优先
4.4G1(72-)
略过
5垃圾回收调优
类加载与字节码(96-)
1.类加载概述
2字节码指令
2.4分析a++(112-)
取决于先执行 iload 还是 iinc
iload 将数存入栈,iinc 将数加减
2.5条件判断指令
全在讲指令,这些指令看的有啥用?
2.11异常处理
finally 将里面的代码进行拷贝,分别放到try ,catch的最后,所以一定会执行
面试题
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-MGoYxq2l-1649992893015)(jvm笔记.assets/image-20220411123619935.png)]
不建议在finally里写return,因为会吞掉ireturn的操作,
3语法糖(130-)
所谓语法糖,其实就是之java编译器 把*.java 源码编译为 *.class字节码的过程中,自动生成和转换的一些代码,主要是为了减轻程序员的负担,算是java编译器给我们的一个额外复利(给糖吃)
3.1默认构造器
public class Candy1 {
}
编译成class后的代码:
public class Candy1{
// 这个无参构造器是编译器帮助我们加上的
public Candy1(){
super();// 即调用父类的 Object 的无参构造方法,即调用java/lang/Object."<init>":()V
}
}
3.2 自动拆装箱
public class Candy2{
public static void main(String[] args) {
Integer x = 1;
int y = x;
}
}
jdk 1.5之前的做法:
public class Candy2{
public static void main(String[] args) {
Integer x = Integer.valueOf(1);
int y = x.intValue();
}
}
3.3 泛型集合取值
java在编译泛型代码后会执行 泛型擦除 操作,即泛型信息在编译为字节码之后就丢失了,实际类型都当做Object类型来处理:
public class Candy3{
public static void main(String[] args) {
List<Integer> list = new ArrayList<>();
list.add(10);// 实际调用的是 List.add(Object e)
Integer x = list.get(0);// 实际调用的是 Object obj = List.get(int index)
}
}
3.4可变参数
3.5foreach循环
int[] array = {1,2,3,4,5};//优化
for (int e : array) { // 优化
sout(e);
}
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-sJRqDdyz-1649992893016)(jvm笔记.assets/image-20220411160340407.png)]
4类加载
4.1加载
-
将类的字节码载入方法区中,内部采用c++的instanceKlass描述java类,它的重要field有:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-bA3QrG4j-1649992893016)(jvm笔记.assets/image-20220412104518119.png)]
-
如果这个类还有父类没有加载,先加载父类
-
加载和链接可能是交替运行的
4.2链接
-
验证:验证类是否符合JVM规范,安全性检查
-
准备:为static变量分配空间,设置默认值
解析
-
解析:将常量池中的符号引用解析为直接引用
4.3初始化
发生的时机
类初始化是 懒惰的
- main方法所在的类,总是会被首先初始化
- 首次访问这个类的静态变量或静态方法时
- 子类初始化,如果父类还没初始化,会引发
- 子类访问父类的静态变量,只会触发父类的初始化
- Class.forName
- new会导致初始化
不会导致初始化的情况
- 访问类的static final静态变量(基本类型和字符串)不会触发初始化
- 类对象.class不会触发初始化
- 创建该类的数组不会触发初始化
- 类加载器的loadClass方法
- Class.forName 的参数2 为false时
5类加载器
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-XsI8EkoU-1649992893016)(jvm笔记.assets/image-20220412144028649.png)]
不同的类加载器,加载不同层级的类
有层级关系,由下到上询问,由上到下加载
5.1启动类加载器
5.2扩展类加载器
5.3双亲委派模式
所谓的双亲委派,就是指调用类加载器的loadClass方法时,查找类的规则
这里的双亲,翻译为上级似乎更合适,因为它们没有继承关系
5.4线程上下文类加载器
SPI,根据接口得到实现类,解耦
5.5自定义类加载器
1)想加载非classpath随意路径中的类文件
2)都是通过接口来实现,希望解耦时,常用在框架设计
3)这些类希望予以隔离,不同应用的同名类都可以加载,不冲突,常见于tomcat容器
步骤:
1.继承ClassLoader父类
2.要遵从双亲委派机制,重写findClass方法
3.读取类文件的字节码
4.调用父类的deineClass方法来加载类
5.使用者调用该类加载器的loadClass方法
6 运行期优化
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ujqRKHbt-1649992893016)(jvm笔记.assets/image-20220412153336627.png)]
热点代码(频繁执行的)编译,
其他解释。
逃逸分析
不会用到的对象不会被创建。c2编译
6.1方法内联
常量折叠
如果发现热点代码,并且长度不太长时,会进行内联,所谓的内联就是把方法内代码拷贝、粘贴到调用者的位置
字段优化
反射优化
内存模型 Java Memory Model
JMM定义了一套在多线程读写共享数据时,对数据的可见性、有序性和原子性的规则和保障
1.1原子性(167-)
原子性:synchronized关键字
1.3解决方法
执行原子操作
2可见性
2.2解决办法
volatile (易变关键字)
它可以用来修饰成员变量和静态成员变量,他可以避免线程从自己的工作缓存中查找变量的值,必须到主存中获取它的值,线程操作volatile变量都是直接操作主存
适用于一个写,多个读,保证可见性,不报证原子性