JVM
Base on:https://www.bilibili.com/video/BV1yE411Z7AP?p=3&spm_id_from=pageDriver
1、引言
什么是JVM?
定义:Java Virtual Machine - java程序的运行环境(java二进制字节码的运行环境)
好处:
- 一次编写,到处运行
- 主动内存管理,垃圾回收功能
- 数字下标越界检查
- 多态
比较:
JVM、JRE、JDK
学习JVM有什么用?
- 面试
- 理解底层的实现原理
- 中高级程序员的必备技能
常见的JVM
学习路线
2、内存结构
2.1 程序计数器
2.1.1 定义
Program Counter Register 程序计数器(寄存器)
2.1.2 作用
作用:记住下一条jvm指令的执行地址
扫描二维码关注公众号,回复:
15376343 查看本文章
特点:
- 是线程私有的
- 不会存在内存溢出
2.2 虚拟机栈
2.2.1 定义
Java Virtual Machine Stacks(Java 虚拟机栈)
- 每个线程运行时需要内存,称为虚拟机栈
- 每个栈由多个栈帧(Frame)组成、对应着每次方法调用时所占用的内存
- 每个线程只能有一个活动栈帧,对应着正在执行的那个方法
演示
package com.caopeng.jvm.t1;
/**
* @author Crescent_P
* @date 2022-01-05 19:34
*/
public class Demo1_1 {
public static void main(String[] args) {
method1();
}
private static void method1() {
method2(1,2);
}
private static int method2(int a, int b) {
int c = a + b;
return c;
}
}
问题辨析
-
垃圾回收是否涉及栈内存?
答:不涉及,因为栈内存是一次次方法调用产生的栈帧内存、每次方法调用完之后,栈帧会被弹栈,会自动地被回收掉。
-
栈内存的分配越大越好吗?
答:不是,因为物理内存是一定的,栈内存越大、线程数会变少。
-
方法内的局部变量是否线程安全?
答:判断是否线程安全、就是看多个线程对这个变量是否共享。一个线程对应一个虚拟机栈、一个方法对应一个栈帧,那么方法内的局部变量是对于这个线程来说是私有的。
package com.caopeng.jvm.t1;
/**
* @author Crescent_P
* @date 2022-01-05 21:21
* 局部变量的线程安全问题
如果方法内局部变量没有逃离方法的作用范围,那就是线程安全的
如果局部变量引用了对象,并逃离了方法的作用范围,需要考虑线程安全问题
*/
public class Demo1_3 {
public static void main(String[] args) {
StringBuilder sb = new StringBuilder();
sb.append(4);
sb.append(5);
sb.append(6);
// 创建一个新的进程
new Thread(()->{
m2(sb);
}).start();
}
// 不存在线程安全问题
public static void m1(){
// 方法内的局部变量
StringBuilder sb = new StringBuilder();
sb.append(1);
sb.append(2);
sb.append(3);
System.out.println(sb.toString());
}
// 存在线程安全问题
// 作为参数,有可能有其它的线程访问到它
public static void m2(StringBuilder sb){
sb.append(1);
sb.append(2);
sb.append(3);
System.out.println(sb.toString());
}
// 不能
// 会将sb返回,那么其它进程就可能拿到这个sb
public static StringBuilder m3(){
StringBuilder sb = new StringBuilder();
sb.append(1);
sb.append(2);
sb.append(3);
return sb;
}
}
2.2.2 栈内存溢出
栈帧过多导致栈内存溢出
栈帧过大导致栈内存溢出
演示
package com.caopeng.jvm.t1;
/**
* @author Crescent_P
* @date 2022-01-05 21:31
* 显示栈内存溢出
* -Xss256K
*/
public class Demo1_4 {
private static int count;
public static void main(String[] args) {
try{
method1();
}catch(Throwable e){
e.printStackTrace();
System.out.println("count " + count); // count 34205
}
}
// 没有终止条件,会一直递归调用
private static void method1(){
count++;
method1();
}
}
package com.caopeng.jvm.t1.stack;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import java.util.Arrays;
import java.util.List;
/**
* @author Crescent_P
* @date 2022-01-05 21:36
*
* json 数据转化
*/
public class Demo1_5 {
public static void main(String[] args) throws JsonProcessingException {
Dept d = new Dept();
d.setName("Market");
Emp e1 = new Emp();
e1.setName("zhang");
e1.setDept(d);
Emp e2 = new Emp();
e2.setName("li");
e2.setDept(d);
d.setEmps(Arrays.asList(e1,e2));
ObjectMapper mapper = new ObjectMapper();
// 将 部门类转化成json
// 会一直递归,循环引用
// { name : 'Market' , emps : [{ name : 'zhang' ,dept : { name: '' ,emps : [}},]
System.out.println(mapper.writeValueAsString(d));
}
}
// 员工
class Emp{
private String name;
// 转化员工的时候,部门就不转化了
@JsonIgnore
private Dept dept;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Dept getDept() {
return dept;
}
public void setDept(Dept dept) {
this.dept = dept;
}
}
// 部门
class Dept{
private String name;
private List<Emp> emps;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public List<Emp> getEmps() {
return emps;
}
public void setEmps(List<Emp> emps) {
this.emps = emps;
}
}
2.2.3 线程运行诊断
案例1:CPU占用过多
案例2:程序运行很长时间没有结果