Java中的虚拟机栈

一. 定义

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

二. 概述

在这里插入图片描述

  • 每个方法在运行时需要的内存都会创建一个栈帧(Stack Frame)
    用于存储局部变量表、操作数栈、动态链接、方法出口等信息。每一个方法从调用直至执行完成的过程,就对应着一个栈帧在虚拟机栈中入栈到出栈的过程

  • 栈帧( Frame)是用来存储数据和部分过程结果的数据结构,同时也被用来处理动态链接(Dynamic Linking)方法返回值异常分派( Dispatch Exception)栈帧随着方法调用而创建,随着方法结束而销毁— —无论方法是正常完成还是异常完成(抛出了在方法内未被捕获的异常)都算作方法结束

注意:

  1. 线程私有,每个线程都会有Java虚拟机栈
  2. 每个线程运行时所需要的内存空间,称为虚拟机栈
  3. 每个栈由多个栈帧(Frame)组成,对应着每次方法调用时所占用的内存
  4. 每个线程只能有一个活动栈帧,对应着当前正在执行的那个方法

三. 栈的演示

在这里插入图片描述

四. 问题辨析

  1. 垃圾回收是否涉及栈内存?

    • 不需要,因为栈内存就是一次次方法调用所产生的栈帧内存,而栈帧内存在每一次方法调用结束后,都会弹出栈,自动的被回收掉,根本不需要垃圾回收来管理栈内存。
  2. 栈内存分配越大越好吗?

    • 栈内存划分越大,会导致线程数变少,例如一个线程需要1MB栈内存,100MB内存就能划分100个线程,但是如果一个线程需要2MB栈内存,那么100MB内存只能划分50个线程
  3. 方法内的局部变量是否是线程安全?

    • 如果方法内局部变量没有逃离方法的作用范围,他就是线程安全的
    • 如果局部变量引用了对象,并逃离方法的作用范围
    • 在这里插入图片描述

五. 栈内存溢出

  1. 栈帧过多会导致栈内存溢出

在这里插入图片描述

  1. 栈帧过大导致栈内存溢出

在这里插入图片描述
在这里插入图片描述

代码演示

/*
    演示栈内存溢出 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();
    }
}

  1. 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;
    }
}

六. 线程运行诊断

  • 案例1:CPU占用过多

这里需要安装:

  1. VMware Workstation Pro
  2. Xshell 7(免费)
  3. SecureCRT.exe
    在这里插入图片描述
  1. 定位
    在这里插入图片描述

  2. 用top定位哪个进程对cpu的占用过高
    在这里插入图片描述

  3. ps H -eo pid,tid,%cpu | grep 进程id (用ps命令进一步定位是哪个线程引起的cpu占用过高)
    在这里插入图片描述

  4. jstack 进程id
    可以根据线程id 找到有问题的线程,进一步定位到问题代码的源码行号
    在这里插入图片描述

    • 所以java:8,在源程序第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();
    }
}
  • 案例2:程序运行很长时间没有结果
  1. 输入nohup 路径 &进行连接
    在这里插入图片描述
  2. jstack 进程id
    可以根据线程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();
    }

}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

发热的嘤嘤怪(2003计科胜胜同学)

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值