java内存模型详细解析 (下)

简介: java内存模型详细解析

3: istore_2 将int类型值存入局部变量2 -->意思是将int b=2;中的变量b存入局部变量表中第三个位置, 然后让操作数栈中的数字2出栈, 给局部变量表中的b赋值为2


1187916-20200704072526586-1907192425.png


4: iload_1 从局部变量1中装载int类型值--->这句话的意思是, 将操作数1从操作数栈取出, 转入局部变量表中的a, 现在局部变量表中a=1


要想更好的理解iload_1,我们要先来研究程序计数器。


程序计数器


在JVM虚拟机中,程序计数器是其中的一个组成部分。

1187916-20200702055039170-323159396.png

程序计数器是每一个线程独有的, 他用来存放马上要执行的那行代码的内存位置, 也可以叫行号. 我们看到jvm反编译代码里,都会有0 1 2 3这样的位置(如下图), 我们可以将其认为是一个标识.而程序计数器可以简单理解为是记录这些数字的. 而实际上这些数字对应的是内存里的地址

1187916-20200704073505180-1584618851.png


当字节码执行引擎执行到第4行的时候,将执行到4: iload_1, 我们可以简单理解为程序计数器记录的代码位置是4. 我们的方法Math.class是放在方法区的, 由字节码执行引擎执行, 每次执行完一行代码, 字节码执行引擎都会修改程序计数器的位置, 让其向下移动一位

1187916-20200704081714055-651792999.png


java虚拟机为什么要设计程序计数器呢?


因为多线程。当一个线程正在执行, 被另一个线程抢占了cpu, 这时之前的线程就要挂起, 当线程2执行完以后, 再执行线程1. 那么线程1之前执行到哪里了呢? 程序计数器帮我们记录了.


下面执行这句话


4: iload_1 从局部变量1中装载int类型值--> 意思是从局部变量表的第二个位置取出int类型的变量值, 将其放入到操作数栈中.此时程序计数器指向的是4


1187916-20200704083502726-744060454.png


5: iload_2 从局部变量2中装载int类型值-->意思是将局部变量中的第三个int类型的元素b的值取出来, 放到操作数栈, 此时程序计数器指向的是5

1187916-20200704083615433-1567766116.png



6: iadd 执行int类型的加法 ---> 将两个局部变量表中的数取出, 进行加法操作, 此操作是在cpu中完成的, 将执行后的结果3在放入到操作数栈 ,此时程序计数器指向的是6

1187916-20200704084553226-1009721709.png

7: bipush 10 :将一个8位带符号整数压入栈 --> 这句话的意思是将10压入操作数栈


1187916-20200704205853985-690582364.png

我们发现这里的位置是7, 但是下一个就变成了9, 那8哪里去了呢? 其实这里的0 1 2 3 ...都是对应的内存地址, 我们的乘数10也会占用内存空间, 所以, 8的位置存的是乘数10


9: imul 执行int类型的乘法 --> 这个和iadd加法一样, 首先将操作数栈中的3和10取出来, 在cpu里面进行计算, 将计算的结果30在放回操作数栈


乘法操作是在cpu的寄存器中进行计算的. 我们这里说的都是保存在内存中.

1187916-20200704210551100-1522298318.png


10: istore_3 将int类型值存入局部变量表中 ---> 意思是是将c这个变量放入局部变量表, 然后让操作数栈中的30出栈, 赋值给变量c



1187916-20200704211306474-1190369096.png

11: iload_3 从局部变量3中装载int类型值 --> 将局部变量表中取出第4个位置的值30, 装进局部变量表

1187916-20200704211734469-2132779387.png


12: ireturn 从方法中返回int类型的数据 --> 最后将得到的结果c返回.

这个方法中的变量是如何在操作数栈和局部变量表中转换的, 我们就知道了. 现在应该可以理解操作数栈和局部变量表了吧~~~


总结:什么是操作数栈?**


在运算的过程中, 常数1, 2, 10, 也需要有内存空间存放, 那么它存在哪里呢? 就保存在操作数栈里面

操作数栈就是在运行的过程中, 一块临时的内存中转空间


4.3.3 动态链接


在之前说过什么是动态链接: 参考文章:

https://www.cnblogs.com/ITPower/p/13197220.html 搜索:动态链接


静态链接是在程序加载的时候一同被加载进来的. 通常用静态常量, 静态方法等, 因为他们在内存地址中只有一份, 所以, 为了性能, 就直接被加载进来了


而动态链接, 是使用的时候才会被加载进来的链接, 比如compute方法. 只要在执行到math.compute()方法的时候才会真的进行加载.


4.3.4 方法出口


当我们运行完compute()方法以后, 还要返回到main方法的math.comput()方法的位置, 那么他怎么返回回来呢?返回回来以后该执行哪一句代码了呢?在进入compute()方法之前,就在方法出口里记录好了, 我应该如何返回,返回到哪里. 方法出口就是记录一些方法的信息的.


五. 堆和栈的关系



上面研究了compute()方法的栈帧空间,再来看一下main方法的栈帧空间。整体来说,都是一样的,但有一块需要说明一下,那就是局部变量表。来看看下面的代码


public static void main(String[] args) {
  Math math = new Math();
  math.compute();
}

main方法的局部变量和compute()有什么区别呢? main方法中的math是一个对象. 我们知道通常对象是被创建在堆里面的. 而math是在局部变量表中, 记录的是堆中new Math对象的地址。


说的明白一些,math里存放的不是具体的内容,而是实例对象的地址。

1187916-20200705042405702-1730598965.png

那么栈和堆的关系就出来了, 如果栈中有很多new对象, 这些对象是创建在堆里面的. 栈里面存的是这些堆中创建的对象的内存地址。


六. 方法区


我们可以通过javap -v Math.class > Math.txt命令, 打印更详细的jvm反编译后的代码

image.png

这次生成的代码,和使用javap -c生成的代码的区别是多了Constant pool常量池。这些常量池是放在哪里的呢?放在方法区。这里看到的常量池叫做运行时常量池。还有很多其他的常量池,比如:八大数据类型的对象常量池,字符串常量池等。


这里主要理解运行时常量池。运行时常量池放在方法区里。


方法区主要有哪些元素呢?


常量 + 静态变量 + 类元信息(就是类的代码信息)

在Math.class类中, 就有常量和静态常量


public static int initData = 666;
public static User user = new User();

他们就放在方法区里面. 这里面 new User()是放在堆里面的, 在堆中分配了一个内存地址,而user对象是放在方法区里面的. 方法区中user对象指向了在堆中分配的内存空间。


image.png


堆和方法区的关系是: 方法区中对象引用的是堆中new出来的对象的地址

类元信息: Math.class整个类中定义的内容就是类元信息, 也放在方法区。


七. 本地方法栈



本地方法栈是有c++代码实现的方法. 方法名带有native的代码.

比如:


new Thread().start();

这里的start()调用的就是本地方法

1187916-20200705045229125-632195762.png


这就是本地方法

本地方法栈: 运行的时候也需要有内存空间去储存, 这些内存空间就是本地方法栈提供的

1187916-20211008174927503-1286115632.png


每一个线程都会分配一个栈空间,本地方法栈和程序计数器。如上图main线程:包含线程栈,本地方法栈,程序计数器。


相关文章
|
1天前
|
XML JavaScript Java
详解Java解析XML的四种方法
详解Java解析XML的四种方法
|
2天前
|
机器学习/深度学习 算法 Linux
xenomai内核解析--实时内存管理--xnheap
Xenomai是一个实时操作系统(RTOS)层,用于Linux,旨在提供确定性的任务调度和服务。其内存管理机制包括一个名为xnheap的内存池,确保内存分配和释放的时间确定性,以满足硬实时系统的严格需求。
10 0
xenomai内核解析--实时内存管理--xnheap
|
2天前
|
Java
解析java中的数组
解析java中的数组
10 3
|
3天前
|
存储 算法 Java
了解Java内存管理与垃圾回收机制
了解Java内存管理与垃圾回收机制
6 0
|
3天前
|
存储 Java 程序员
Java面向对象编程的基础概念解析
Java面向对象编程的基础概念解析
13 0
|
3天前
|
缓存 Java 编译器
JMM内存模型 volatile关键字解析
JMM内存模型 volatile关键字解析
10 0
|
4天前
|
分布式计算 Java API
Java8 Lambda实现源码解析
Java8的lambda应该大家都比较熟悉了,本文主要从源码层面探讨一下lambda的设计和实现。
|
9天前
|
安全 前端开发 Java
10:基于Servlet模拟用户登录功能的实现与解析-Java Web
10:基于Servlet模拟用户登录功能的实现与解析-Java Web
24 3
|
4天前
|
Java Android开发
Android12 双击power键启动相机源码解析
Android12 双击power键启动相机源码解析
14 0
|
1天前
PandasTA 源码解析(一)(2)
PandasTA 源码解析(一)
7 0

推荐镜像

更多