1,什么是JVM
1.1 定义:
Java Virtual Machine ,是java程序的运行环境(java二进制字节码的运行环境)
1.2 优势:
- 一次开发,到处运行
- 自动内存谷粒,垃圾回收功能
- 数组下标越界检查(防止越界覆盖其他数据结构的内存)
- 多态
1.3 JDK与JRE的关系
经常使用JPS/JAVAC/JAVAP等命令都是属于JDK,不属于JRE,有些服务器上只安装了JRE,也能运行JAVA服务,但是不能通过JPS查看java进程,不能使用jmap等性能检测工具。
2,学习JVM有什么用?
2.1 提升面试竞争力
学成文武艺,货与帝王家,JVM就是竞争力。
2.2 理解底层实现原理
JVM是内功,有深厚的内存,才能练出高强的招式。
2.3 工作必备技能
如何写出高性能的代码,如何设计高性能的应用架构,如何排查生产中出现的问题。只有学习了JVM,理解原理和常用工具,才能实现以上目标。
3,常见的JVM
3.1,实现了JVM规范的应用程序就是JVM
4,学习路线
4.1 Jvm组成部分
- Java 类加载
- Jvm内存结构:方法区、堆、虚拟机栈、PC计数器、本地方法栈
- 执行引擎 :解释器、即时编译器、GC
- 本地方法接口
4.2 学习顺序
5,程序计数器
5.1 Java代码的运行流程
计数器的作用是在解释器读取字节码的过程中,记住当前字节码的地址,程序计数器总是指向下一条字节码指令的位置。
6,程序计数器-特点
6.1 线程私有
容易理解,线程是CPU调度的基本单位,每个线程独立的执行代码,线程会竞争CPU资源,竞争失败会被挂起,挂起之后恢复需要知道下一个指令的地址,所以必须私有。
6.2 唯一不存在内存溢出的区域
7,虚拟机栈
7.1 线程私有
每个线程都有一个虚拟机栈,虚拟机栈是线程私有的
7.2 作用
在代码执行的过程中,会形成一个方法调用链,这个链本质是栈,每个方法调用在虚拟机中都会有一个栈帧与之对应,调用结束栈帧会被弹出。
7.3 结构
- 局部变量表
- 操作数栈
- 返回地址(调用当前方法的地址)
8,Idea演示栈帧
使用Idea Debug程序时,可以看到方法调用链,就是虚拟机栈的外在表现形式,每个虚拟机栈都会对应一个局部变量表,存放局部变量的值,为了配合代码的执行,还必须有一个操作数栈,操作数栈在Idea中未通过UI呈现。
9~11,问题辨析1
1,垃圾回收释放设计栈内存?
不需要,栈帧在使用结束后就会被释放,不会继续存在,所以无需垃圾回收
2,栈内存越大越好吗?
- 通过-Xss125m 设置虚拟机栈的大小
- 不是越大越好,虚拟机栈是线程私有的,每个线程都有虚拟机栈,
总的虚拟机栈大小=线程数*虚拟机栈大小
,设置的就是单个虚拟机栈大小,所以虚拟机栈设置的太大,受限于内存总大小,线程数就会受到限制,所以虚拟机栈并不是越大越好。
3,方法内局部变量是否线程安全?
- m1的变量sb是线程安全的,在方法内定义、方法内使用,生命周期在方法内部
- m2的变量sb不是线程安全的,其生命周期大于方法的周期,其作用范围大于方法内部的作用范围,可能会被其他线程共享
- m3的变量sb虽然是在方法内部创建的,但是其引用会传递给其他方法,可能被其他线程共享,所以不是线程安全的
12~13 栈内存溢出
-
方法调用链太深,栈帧太多,比如递归调用、死循环调用,会导致栈内存溢出
-
栈太大,一个栈帧把栈内存撑爆。
复现 使用 -Xss1k,把栈内存设置为1k,创建一个大于1k的局部变量,就会栈内存溢出
- 第三方包导致栈内存溢出:比如Dept内引用Emp类对象,Emp对象又引用Dept对象,在Json序列化时就可能导致栈内存溢出