文章详情

短信预约-IT技能 免费直播动态提醒

请输入下面的图形验证码

提交验证

短信预约提醒成功

JVM内存结构:程序计数器、虚拟机栈、本地方法栈

2024-04-02 19:55

关注

一、JVM 入门介绍

JVM 定义

Java Virtual Machine,JAVA程序的运行环境(JAVA二进制字节码的运行环境)

JVM 优势

在这里插入图片描述

:我们笔记所使用的的是HotSpot 版本

JVM JRE JDK的比较

JVM JRE JDK的区别:

img

学习步骤

学习顺序如下图:(由简到难)

在这里插入图片描述

二、内存结构

整体架构

在这里插入图片描述

1、程序计数器(寄存器)

Program Counter Register

在这里插入图片描述

1.1 作用

程序计数器用于保存JVM中下一条所要执行的指令的地址


0:getstatic #20 					 // PrintStream out = System.out;
1:astore_1 							// --
2:aload_1 							// out.println(1);
3:iconst_1 							// --
4:invokevirtual #26 				 // --
5:aload_1 						    // out.println(2);
6:iconst_2 							// --
7:invokevirtual #26 				 // --
8:aload_1 						    // out.println(3);
9:iconst_3 						    // --
10:invokevirtual #26 				 // --
11:aload_1 							// out.println(4);
12:iconst_4 						// --
13:invokevirtual #26 				 // --
14:aload_1 						    // out.println(5);
15:iconst_5 						// --
16:invokevirtual #26 				 // --
return

在这里插入图片描述

Java指令执行流程:

1.2 特点

在这里插入图片描述

2、虚拟机栈

Java Virtual Machine Stacks

在这里插入图片描述

2.1 定义

2.2 演示

在这里插入图片描述

代码



public class Demo01 {
    public static void main(String[] args) {
        methodA();
    }
    private static void methodA() {
        methodB(1, 2);
    }
    private static int methodB(int a, int b) {
        int c = a + b;
        return c;
    }
}

我们打断点来Debug 一下看一下方法执行的流程:

在这里插入图片描述

接这往下走,使方法B执行完毕:

在这里插入图片描述

然后方法A执行完毕,其对应的栈帧出栈,main方法对应的栈帧为活动栈帧;最后main执行完毕 栈帧出栈,虚拟机栈为空,代码运行结束!

2.3 面试问题辨析

在这里插入图片描述

3.方法内的局部变量是否是线程安全的?

在这里插入图片描述


在这里插入图片描述

从图中得出:局部变量如果是静态的可以被多个线程共享,那么就存在线程安全问题。如果是非静态的只存在于某个方法作用范围内,被线程私有,那么就是线程安全的!

看一个案例:



public class Demo02 {
    public static void main(String[] args) {// main 函数主线程
        StringBuilder sb = new StringBuilder();
        sb.append(4);
        sb.append(5);
        sb.append(6);
        new Thread(() -> {// Thread新创建的线程
            m2(sb);
        }).start();
    }
    public static void m1() {
        // sb 作为方法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 作为方法m2()外部的传递来的参数,sb 不在方法m2()的作用范围内
        // 不是线程私有的 ---> 非线程安全
        sb.append(1);
        sb.append(2);
        sb.append(3);
        System.out.println(sb.toString());
    }
    public static StringBuilder m3() {
        // sb 作为方法m3()内部的局部变量,是线程私有的
        StringBuilder sb = new StringBuilder();// sb 为引用类型的变量
        sb.append(1);
        sb.append(2);
        sb.append(3);
        return sb;// 然而方法m3()将sb返回,sb逃离了方法m3()的作用范围,且sb是引用类型的变量
        // 其他线程也可以拿到该变量的 ---> 非线程安全
        // 如果sb是非引用类型,即基本类型(int/char/float...)变量的话,逃离m3()作用范围后,则不会存在线程安全
    }
}

该面试题答案:

如果方法内局部变量没有逃离方法的作用范围,则是线程安全

如果局部变量引用了对象,并逃离了方法的作用范围,则需要考虑线程安全问题

2.4 内存溢出

Java.lang.stackOverflowError 栈内存溢出

发生原因

在这里插入图片描述

举2个案例:

案例1:



public class Demo03 {
    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();// 方法无限递归,不断产生栈帧 到虚拟机栈
    }
}
最后输出结果:
java.lang.StackOverflowError
	at com.haust.jvm_study.demo.Demo03.method1(Demo03.java:21)
     ...
     ...
39317// 栈帧个数,不同的虚拟机大小能存放的栈帧数量不一样

我们可以通过修改参数来指定虚拟机栈内存大小

在这里插入图片描述

当我们将虚拟机栈内存缩小到指定的256k的时候再运行Demo03后,会得到其栈内最大栈帧数为:3816 远小于原来的39317

案例2:



public class Demo04 {
    public static void main(String[] args) throws JsonProcessingException {
        Dept d = new Dept();
        d.setName("Market");
        Emp e1 = new Emp();
        e1.setName("csp");
        e1.setDept(d);
        Emp e2 = new Emp();
        e2.setName("hzw");
        e2.setDept(d);
        d.setEmps(Arrays.asList(e1, e2));
        // 输出结果:{"name":"Market","emps":[{"name":"csp"},{"name":"hzw"}]}
        ObjectMapper mapper = new ObjectMapper();// 要导入jackson包
        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.5 线程运行诊断

案例1:CPU占用过高

在这里插入图片描述


在这里插入图片描述

在这里插入图片描述

在这里插入图片描述


在这里插入图片描述

我们可以看到上图中的thread1 线程一直在运行(runnable)中,说明就是它占用了较高的CPU内存;

在这里插入图片描述


3、本地方法栈

在这里插入图片描述

在这里插入图片描述

一些带有native 关键字的方法就是需要JAVA去调用本地的C或者C++方法,因为JAVA有时候没法直接和操作系统底层交互,所以需要用到本地方法!

如图:


在这里插入图片描述


在这里插入图片描述

4、总结

这篇文章的内容就到这了,希望大家多多关注编程网的其他内容!

阅读原文内容投诉

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

软考中级精品资料免费领

  • 历年真题答案解析
  • 备考技巧名师总结
  • 高频考点精准押题
  • 2024年上半年信息系统项目管理师第二批次真题及答案解析(完整版)

    难度     813人已做
    查看
  • 【考后总结】2024年5月26日信息系统项目管理师第2批次考情分析

    难度     354人已做
    查看
  • 【考后总结】2024年5月25日信息系统项目管理师第1批次考情分析

    难度     318人已做
    查看
  • 2024年上半年软考高项第一、二批次真题考点汇总(完整版)

    难度     435人已做
    查看
  • 2024年上半年系统架构设计师考试综合知识真题

    难度     224人已做
    查看

相关文章

发现更多好内容

猜你喜欢

AI推送时光机
位置:首页-资讯-后端开发
咦!没有更多了?去看看其它编程学习网 内容吧
首页课程
资料下载
问答资讯