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

本文涉及的产品
全局流量管理 GTM,标准版 1个月
云解析 DNS,旗舰版 1个月
公共DNS(含HTTPDNS解析),每月1000万次HTTP解析
简介: 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线程:包含线程栈,本地方法栈,程序计数器。


相关文章
|
29天前
|
安全 Java 程序员
深入理解Java内存模型与并发编程####
本文旨在探讨Java内存模型(JMM)的复杂性及其对并发编程的影响,不同于传统的摘要形式,本文将以一个实际案例为引子,逐步揭示JMM的核心概念,包括原子性、可见性、有序性,以及这些特性在多线程环境下的具体表现。通过对比分析不同并发工具类的应用,如synchronized、volatile关键字、Lock接口及其实现等,本文将展示如何在实践中有效利用JMM来设计高效且安全的并发程序。最后,还将简要介绍Java 8及更高版本中引入的新特性,如StampedLock,以及它们如何进一步优化多线程编程模型。 ####
31 0
|
4天前
|
人工智能 自然语言处理 Java
FastExcel:开源的 JAVA 解析 Excel 工具,集成 AI 通过自然语言处理 Excel 文件,完全兼容 EasyExcel
FastExcel 是一款基于 Java 的高性能 Excel 处理工具,专注于优化大规模数据处理,提供简洁易用的 API 和流式操作能力,支持从 EasyExcel 无缝迁移。
47 9
FastExcel:开源的 JAVA 解析 Excel 工具,集成 AI 通过自然语言处理 Excel 文件,完全兼容 EasyExcel
|
11天前
|
存储 缓存 Java
Java 并发编程——volatile 关键字解析
本文介绍了Java线程中的`volatile`关键字及其与`synchronized`锁的区别。`volatile`保证了变量的可见性和一定的有序性,但不能保证原子性。它通过内存屏障实现,避免指令重排序,确保线程间数据一致。相比`synchronized`,`volatile`性能更优,适用于简单状态标记和某些特定场景,如单例模式中的双重检查锁定。文中还解释了Java内存模型的基本概念,包括主内存、工作内存及并发编程中的原子性、可见性和有序性。
Java 并发编程——volatile 关键字解析
|
8天前
|
Java 数据库连接 Spring
反射-----浅解析(Java)
在java中,我们可以通过反射机制,知道任何一个类的成员变量(成员属性)和成员方法,也可以堆任何一个对象,调用这个对象的任何属性和方法,更进一步我们还可以修改部分信息和。
|
29天前
|
存储 监控 算法
深入探索Java虚拟机(JVM)的内存管理机制
本文旨在为读者提供对Java虚拟机(JVM)内存管理机制的深入理解。通过详细解析JVM的内存结构、垃圾回收算法以及性能优化策略,本文不仅揭示了Java程序高效运行背后的原理,还为开发者提供了优化应用程序性能的实用技巧。不同于常规摘要仅概述文章大意,本文摘要将简要介绍JVM内存管理的关键点,为读者提供一个清晰的学习路线图。
|
1月前
|
存储 算法 Java
Java内存管理深度解析####
本文深入探讨了Java虚拟机(JVM)中的内存分配与垃圾回收机制,揭示了其高效管理内存的奥秘。文章首先概述了JVM内存模型,随后详细阐述了堆、栈、方法区等关键区域的作用及管理策略。在垃圾回收部分,重点介绍了标记-清除、复制算法、标记-整理等多种回收算法的工作原理及其适用场景,并通过实际案例分析了不同GC策略对应用性能的影响。对于开发者而言,理解这些原理有助于编写出更加高效、稳定的Java应用程序。 ####
|
28天前
|
存储 监控 算法
Java内存管理的艺术:深入理解垃圾回收机制####
本文将引领读者探索Java虚拟机(JVM)中垃圾回收的奥秘,解析其背后的算法原理,通过实例揭示调优策略,旨在提升Java开发者对内存管理能力的认知,优化应用程序性能。 ####
42 0
|
2月前
|
监控 Java 应用服务中间件
高级java面试---spring.factories文件的解析源码API机制
【11月更文挑战第20天】Spring Boot是一个用于快速构建基于Spring框架的应用程序的开源框架。它通过自动配置、起步依赖和内嵌服务器等特性,极大地简化了Spring应用的开发和部署过程。本文将深入探讨Spring Boot的背景历史、业务场景、功能点以及底层原理,并通过Java代码手写模拟Spring Boot的启动过程,特别是spring.factories文件的解析源码API机制。
87 2
|
10天前
|
存储 设计模式 算法
【23种设计模式·全精解析 | 行为型模式篇】11种行为型模式的结构概述、案例实现、优缺点、扩展对比、使用场景、源码解析
行为型模式用于描述程序在运行时复杂的流程控制,即描述多个类或对象之间怎样相互协作共同完成单个对象都无法单独完成的任务,它涉及算法与对象间职责的分配。行为型模式分为类行为模式和对象行为模式,前者采用继承机制来在类间分派行为,后者采用组合或聚合在对象间分配行为。由于组合关系或聚合关系比继承关系耦合度低,满足“合成复用原则”,所以对象行为模式比类行为模式具有更大的灵活性。 行为型模式分为: • 模板方法模式 • 策略模式 • 命令模式 • 职责链模式 • 状态模式 • 观察者模式 • 中介者模式 • 迭代器模式 • 访问者模式 • 备忘录模式 • 解释器模式
【23种设计模式·全精解析 | 行为型模式篇】11种行为型模式的结构概述、案例实现、优缺点、扩展对比、使用场景、源码解析
|
10天前
|
设计模式 存储 安全
【23种设计模式·全精解析 | 创建型模式篇】5种创建型模式的结构概述、实现、优缺点、扩展、使用场景、源码解析
结构型模式描述如何将类或对象按某种布局组成更大的结构。它分为类结构型模式和对象结构型模式,前者采用继承机制来组织接口和类,后者釆用组合或聚合来组合对象。由于组合关系或聚合关系比继承关系耦合度低,满足“合成复用原则”,所以对象结构型模式比类结构型模式具有更大的灵活性。 结构型模式分为以下 7 种: • 代理模式 • 适配器模式 • 装饰者模式 • 桥接模式 • 外观模式 • 组合模式 • 享元模式
【23种设计模式·全精解析 | 创建型模式篇】5种创建型模式的结构概述、实现、优缺点、扩展、使用场景、源码解析

热门文章

最新文章

推荐镜像

更多