JVM - 结合代码示例彻底搞懂Java内存区域_线程栈 | 本地方法栈 | 程序计数器


在这里插入图片描述

Pre

JVM-01Java内存区域与内存溢出异常(上)【运行时区域数据】

JVM-02内存区域与内存溢出异常(中)【hotspot虚拟机对象】

JVM-03内存区域与内存溢出异常(下)【OutOfMemoryError案例】


运行时数据区总览

在这里插入图片描述

字节码文件被装载子系统装载到JVM中,字节码执行引擎负责执行这些字节码文件。
装载子系统和执行引擎都是C++的实现。

装载子系统: JVM-白话聊一聊JVM类加载和双亲委派机制源码解析

我们重点关注下运行时数据区域 ,先关注线程私有的这3个部分。


线程栈

概要

没给方法被执行的时候,JVM都会同步创建一个栈帧。

这个栈和数据结构的栈结构是一样的, FILO .

举个例子 ,方法A 中调用了方法B , 代码先执行方法A ,此时方法A 入栈, 然后调用方法B,这个时候方法B入栈 。 当方法B执行结束后,方法B出栈,回到方法A执行的地方,方法A继续执行,执行结束 ,方法A出栈。

在这里插入图片描述


栈内部主要组成部分

【Java代码】

   public int doSomething() {
        int a = 1 ;
        int b = 2 ;
        int c = (a + b) * 10 ;
        return c;
    }

【javap -c 反汇编】

如何操作的,见文末 ,JVM字节码指令集也见文末

  public int doSomething();
    Code:
       0: iconst_1
       1: istore_1
       2: iconst_2
       3: istore_2
       4: iload_1
       5: iload_2
       6: iadd
       7: bipush        10
       9: imul
      10: istore_3
      11: iload_3
      12: ireturn
}

0: iconst_1
1: istore_1 【i —> int类型】

iconst_1 是个什么鬼? 查查操作手册
在这里插入图片描述
istore_1

在这里插入图片描述

iconst_0 和 istore_0 是默认存放调用该方法的对象。

这里就涉及到两个组成部分 【局部变量】 + 【操作数栈】

局部变量

0x04  iconst_1  将 int1 推送至栈顶
0x3c  istore_1  将栈顶 int 型数值存入第二个本地变量
0x05  iconst_2  将 int2 推送至栈顶
0x3d  istore_2  将栈顶 int 型数值存入第三个本地变量
 

比对代码

  int a = 1 ;
  int b = 2 ;

iconst_1 , 将 int 1 压入操作数栈 , istore_1 将栈顶 int 型数值存入第二个本地变量 ,这个时候 会先将 1 出栈,然后存入局部变量表。

istore_1 、istore_2 存入本地变量,就是放到了局部变量表。


操作数栈

0x04  iconst_1  将 int1 推送至栈顶
0x05  iconst_2  将 int2 推送至栈顶

这两步的意思 就是将代码中的 a 和 b 对应的值 1 和 2 压入 操作数栈 。

这个操作数栈 本身也是栈结构, FILO . 入栈 出栈


继续

 int c = (a + b) * 10 ;
 return c;
 4: iload_1    将第二个 int 型本地变量推送至栈顶  ----> a的值 1 入栈 (操作数栈)
 5: iload_2    将第三个 int 型本地变量推送至栈顶  ----> b的值 2 入栈 (操作数栈)
 6: iadd       将栈顶两 int 型数值相加并将结果压入栈顶  ---->  计算 a+b =3,结果压入栈顶 (a ,b 出栈,计算结果,然后将a+b的结果压入操作数栈)
 7: bipush        10      将单字节的常量值(-128~127)推送至栈顶  -----> 10 入栈 操作数栈)
 9: imul       将栈顶两 int 型数值相乘并将结果压入栈顶  ----> 计算乘积 3 * 10 ,将30压入操作数栈
 10: istore_3  将栈顶 int 型数值存入第四个本地变量  ------>  给 c赋值 
 11: iload_3   将第四个 int 型本地变量推送至栈顶  ----> 压入操作数栈 
 12: ireturn   从当前方法返回 int  ----> 返回

上面的行号 7 到9 ? 没有9 。 我们这个常量10 也要占位置嘛 。

计算 结果肯定是CPU执行的嘛 ,只不过数据是存放在内存中的。

简言之,操作数栈,是程序运行期间需要临时存放数据的内存空间。


动态链接

符号引用 替换为 直接引用。

我们知道在类装载的过程中,有个过程是【解析】

在这里插入图片描述
JVM-白话聊一聊JVM类加载和双亲委派机制源码解析

举个例子

 public static void main(String[] args) {
        Artisan artisan = new Artisan();
        artisan.doSomething();
    }

简单说当应用执行到artisan.doSomething()方法( 非静态方法),需要找到这个方法在方法区的常量池中的具体的位置,这个过程就是动态链接

方法区#运行时常量池 ,是方法区的一部分。 Class文件中的常量池表用于存放编译期间生成的各种字面量和符号引用,这部分内容将在类加载后放到方法区的运行时常量池中。


方法出口

   public static void main(String[] args) {
        Artisan artisan = new Artisan();
        artisan.doSomething();
    }

    public int doSomething() {
        int a = 1 ;
        int b = 2 ;
        int c = (a + b) * 10 ;
        return c;
    }

doSomething方法执行完要回到main方法中,方法出口中记录的就是记录main方法中的位置,不记录的话 ,不知道回到main方法中的哪一行继续执行哇~


小结

在这里插入图片描述


程序计数器

简单理解,可以理解为 记录程序执行的位置。

线程私有。

Java多线程,当线程A没有抢到CPU的执行权,如果没记录程序执行的位置,等下次抢到CPU执行权的时候,这尼玛咋弄? 重新开始执行吗?

显然是不行的,所以需要程序计数器来给每个线程的执行到的行号做下标记。各个现场的程序计数器互不影响,独立存储。

我们来看看javap -c 处理的反汇编

在这里插入图片描述

简单理解,可以理解为上面的行号, 实际上存储的是这行代码对应在内存中的指针位置。

字节码 由谁来执行? 肯定是字节码执行引擎嘛 ,所以 字节码执行引擎肯定知道程序的执行位置,这样 字节码执行引擎和程序计数器就关联起来了。


本地方法栈

native 方法 底层C++的


测试demo

package com.gof.test;

 
public class Artisan {


    public static void main(String[] args) {
        Artisan artisan = new Artisan();
        artisan.doSomething();
    }

    public int doSomething() { // public 类型
        int a = 1 ;
        int b = 2 ;
        int c = (a + b) * 10 ;
        return c;
    }
}

javap

用法: javap <options> <classes>
其中, 可能的选项包括:
  -help  --help  -?        输出此用法消息
  -version                 版本信息
  -v  -verbose             输出附加信息
  -l                       输出行号和本地变量表
  -public                  仅显示公共类和成员
  -protected               显示受保护的/公共类和成员
  -package                 显示程序包/受保护的/公共类
                           和成员 (默认)
  -p  -private             显示所有类和成员
  -c                       对代码进行反汇编
  -s                       输出内部类型签名
  -sysinfo                 显示正在处理的类的
                           系统信息 (路径, 大小, 日期, MD5 散列)
  -constants               显示最终常量
  -classpath <path>        指定查找用户类文件的位置
  -cp <path>               指定查找用户类文件的位置
  -bootclasspath <path>    覆盖引导类文件的位置

-c 对代码进行反汇编

 E:\Program Files\Java\jdk1.8.0_161\bin> ./javap -c D:\IdeaProjects\GOF23\target\classes\com\gof\test\Artisan.class > Artisan.txt

查看 Artisan.txt

Compiled from "Artisan.java"
public class com.gof.test.Artisan {
  public com.gof.test.Artisan();
    Code:
       0: aload_0
       1: invokespecial #1                  // Method java/lang/Object."<init>":()V
       4: return

  public static void main(java.lang.String[]);
    Code:
       0: new           #2                  // class com/gof/test/Artisan
       3: dup
       4: invokespecial #3                  // Method "<init>":()V
       7: astore_1
       8: aload_1
       9: invokevirtual #4                  // Method doSomething:()I
      12: pop
      13: return

  public int doSomething();
    Code:
       0: iconst_1
       1: istore_1
       2: iconst_2
       3: istore_2
       4: iload_1
       5: iload_2
       6: iadd
       7: bipush        10
       9: imul
      10: istore_3
      11: iload_3
      12: ireturn
}

JVM字节码指令集手册

下载地址戳这里–>提取码:9ru5

©️2020 CSDN 皮肤主题: Age of Ai 设计师: meimeiellie 返回首页
实付0元
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值