JVM学习笔记——内存结构篇( 二 )

  • 但是我们的jvm代码的位置不是顺序排列的,所以这时我们每个线程都需要一个程序计数器来记录下一个jvm的位置
  • 我们将该jvm指令传给解释器后,解释器将其处理的同时程序计数器也接收到下一个地址,进行jvm位置更新
  • 同时我们也强调一点:
    • 程序计数器只是逻辑上的概念,我们通常采用寄存器来充当一个程序计数器
    • 因为寄存器的读取速度是最快的,我们可以快速保存并且读出物理地址位置来进行交互
    虚拟机栈这小节我们来介绍JVM内存结构中的虚拟机栈
    栈简介我们首先来回顾栈的概念:
    JVM学习笔记——内存结构篇

    文章插图
    我们的栈先进后出,用于存储程序中的部分信息
    虚拟机栈简介我们的虚拟机栈和栈的基本原理相同,但存储的东西就不尽相同了:
    • 虚拟机栈也是绑定线程的,每个线程有且仅有一个虚拟机栈
    • 虚拟机栈中存储着栈帧,可以存在有多个栈帧,栈帧就是每个方法运行时所需要的内存
    我们给出虚拟机的概念:
    • 每个线程运行时所需要的内存,称为虚拟机栈
    • 每个线程只能有一个活动栈帧,对应着当前正在执行的那个方法
    • 每个栈由多个栈帧(Frame)组成,对应着每次方法调用时所占用的内存
    虚拟机栈详细介绍我们给出一段Java代码来进行展示:
    package cn.itcast.jvm.t1.stack;/** * 演示栈帧 */public class Demo1_1 {public static void main(String[] args) throws InterruptedException {method1();method3();}private static void method1() {method2(1, 2);}private static int method2(int a, int b) {int c =a + b;return c;}private static int method3() {return 1;}}我们进行简单的介绍:
    • 这个程序就是一个线程
    • 这三个方法分别就对应着一个栈帧
    • 我们调用main,mian进入栈,main中又调用method1,method1进入栈,method1调用method2,所以method2进入栈
    • 注意我们的method3不包含在method1的循环中,所以我们会先将前置栈帧都排除后,然后在main栈帧的上方进行累加method3
    我们在执行过程中如果采用debug模式是可以看到Frames,这个就是表示的栈帧:
    JVM学习笔记——内存结构篇

    文章插图
    虚拟机栈问题解释我们针对虚拟机栈提出了三个问题,下面进行解释:
    1. 垃圾回收是否会涉及栈内存
    /*答案是:否,因为栈是属于线程内内存,栈具有自动回收功能*/
    1. 栈内存是否是越大越好
    /*答案是:否,如果jvm设置的内存过大,就会导致其它程序所占用的内存小 。*/
    1. 方法内的局部变量是否线程安全
    /**答案是:根据实际情况而定,我们通过判断作用范围来进行线程安全判断首先我们需要介绍两个词汇:- StringBuffer 用于多线程,保证多线程安全,但效率较慢- StringBuilder 用于单线程,无法保证但线程安全,效率较快**/// 我们下面给出一个简单示例:// 示例1:下面的变量x是属于方法中的变量,属于局部变量,因此不会出现线程安全问题package cn.itcast.jvm.t1.stack;/** * 局部变量的线程安全问题 */public class Demo1_18 {// 多个线程同时执行此方法static void m1() {int x = 0;for (int i = 0; i < 5000; i++) {x++;}System.out.println(x);}}// 示例2:下面我们通过StringBuilder来进行安全问题测试(因为StringBuilder不具有线程安全保护)// m1:StringBuilder创建在方法内部,也只在方法内部使用,属于局部变量,不具有线程安全问题// m2:StringBuilder来自于外部,变量作用范围越界,具有线程安全问题// m3:StringBuilder返回至外部,变量作用范围越界,具有线程安全问题package cn.itcast.jvm.t1.stack;/** * 局部变量的线程安全问题 */public class Demo1_17 {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());}public static StringBuilder m3() {StringBuilder sb = new StringBuilder();sb.append(1);sb.append(2);sb.append(3);return sb;}}

    经验总结扩展阅读