JVM Stacks 虚拟机栈先进后出,后进先出

每个方法在运行时需要的内存都会创建一个栈帧(Stack Frame)
用于存储局部变量表、操作数栈、动态链接、方法出口等信息。每一个方法从调用直至执行完成的过程,就对应着一个栈帧在虚拟机栈中入栈到出栈的过程
栈帧( Frame)是用来存储数据和部分过程结果的数据结构,同时也被用来处理动态链接(Dynamic Linking)、 方法返回值和异常分派( Dispatch Exception)。栈帧随着方法调用而创建,随着方法结束而销毁— —无论方法是正常完成还是异常完成(抛出了在方法内未被捕获的异常)都算作方法结束
注意:
线程私有,每个线程都会有Java虚拟机栈每个线程运行时所需要的内存空间,称为虚拟机栈每个栈由多个栈帧(Frame)组成,对应着每次方法调用时所占用的内存每个线程只能有一个活动栈帧,对应着当前正在执行的那个方法

垃圾回收是否涉及栈内存?
栈内存分配越大越好吗?
方法内的局部变量是否是线程安全?

栈帧过多会导致栈内存溢出栈帧过大导致栈内存溢出代码演示
/*
演示栈内存溢出 StackOverflowError
-Xss256k
*/
public class Demo2 {
private static int count;
public static void main(String[] args) {
try {
method1();
} catch (Throwable e) {
e.printStackTrace();
System.out.println(count);
}
}
private static void method1() {
count++;
method1();
}
}
json数据转换也会出现注意:@JsonIgnore注解,假如把员工json化时,遇到部门属性直接略过不转该属性
import java.util.Arrays;
import java.util.List;
/*
json 数据转换
*/
public class Demo2 {
public static void main(String[] args) throws JsonprocessingException {
Dept d=new Dept();
d.setName("鸡你太美");
Emp e1=new Emp();
e1.setName("菜徐琨");
e1.setDept(d);
Emp e2=new Emp();
e2.setName("小黑子");
e2.setDept(d);
d.setEmps(Arrays.asList(e1, e2));
ObjectMapper mapper=new ObjectMapper();
System.out.println(mapper.writeValueAsString(d));
}
}
class Emp{
private String name;
private Dept dept;
public Emp() {
}
public Emp(String name, Dept dept) {
this.name = name;
this.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 Dept() {
}
public Dept(String name, List<Emp> emps) {
this.name = name;
this.emps = 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;
}
}
这里需要安装:
- VMware Workstation Pro
- Xshell 7(免费)
- SecureCRT.exe
定位

用top定位哪个进程对cpu的占用过高

ps H -eo pid,tid,%cpu | grep 进程id (用ps命令进一步定位是哪个线程引起的cpu占用过高)

jstack 进程id
可以根据线程id 找到有问题的线程,进一步定位到问题代码的源码行号

8行出现问题
public class Demo2 {
public static void main(String[] args) {
// Thread(ThreadGroup group, Runnable target, String name) 分配一个新的 Thread对象,使其具有 target作为其运行对象,具有指定的 name作为其名称,属于 group引用的线程组。
new Thread(null,()->{
System.out.println("1...");
while(true) {
}},"菜徐琨01").start();
new Thread(null,()->{
System.out.println("2...");
try {
Thread.sleep(1000000L);
} catch (InterruptedException e) {
e.printStackTrace();
}
},"菜徐琨02").start();
new Thread(null,()->{
System.out.println("3...");
try {
Thread.sleep(1000000L);
} catch (InterruptedException e) {
e.printStackTrace();
}
},"菜徐琨03").start();
}
}
nohup 路径 &进行连接
jstack 进程id

class A extends Thread {
}
class B extends Thread {
}
public class Demo3 {
static A a = new A();
static B b = new B();
public static void main(String[] args) throws InterruptedException {
new Thread(() -> {
synchronized (a) {
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (b) {
System.out.println("我获得了a和b");
}
}
}).start();
Thread.sleep(1000);
new Thread(() -> {
synchronized (b) {
synchronized (a) {
System.out.println("我获得了a和b");
}
}
}).start();
}
}