参考链接: JVM如何工作–JVM体系结构

JVM简介        JVM是Java程序得以运行的平台,也是Java程序可以跨平台的底层支撑,从整体上来看,JVM的主要功能可以分为加载和执行两大块。其中类加载器负责.class文件的寻址与加载,执行引擎负责字节码指令执行及内存的管理等等。下面是JVM一个经典的体系结构图

类加载系统:关于类加载体系的详细说明在另一博客https://blog.csdn.net/w1673492580/article/details/81838835

运行时数据区宏观角度:         从宏观角度来看,JVM运行时数据区的各部分划分很明显,方法区用于存储类数据,堆用于存储Java程序在运行过程中创建的所有对象,栈和PC寄存器属于线程独有,栈代表线程执行过程中所有方法调用信息(比如比如入参、局部变量、中间结果,返回信息等等),PC寄存器即计数器,代表指令在主存中的地址,每执行一条指令后PC+1,即指向下一条指令。本地方法栈即通过Java调用本地方法时的相关信息,但与调用Java方法不一样,调用Java方法时JVM会当前Java栈中压入一个新的栈桢,而调用本地方法则不会修改Java栈,从这个角度看,本地方法栈可以理解成JVM运行时数据区的一个扩展。

微观角度:堆:Java程序在运行过程中(加载也是运行的一部分)创建的所有对象都位于堆内存中,且JVM中堆内存在设计上是多线程共享的,所以堆中数据的访问也必须进行安全控制;        由于运行期间很可能会创建大量的对象,而且大部分对象都是小而短的(占用空间小及生命周期短),所以堆内存的管理也尤其重要,但JVM中并没有释放对象的指令,这表示开发中不能通过代码去管理对象的释放,所以JVM内置了垃圾回收器来管理,堆也是垃圾回收主要集中的地方(对于栈由于栈桢的大小可以在编译期就根据类结构数据确定,所以这部分的回收具有一定的确定性)。       在Java代码中,通过obj.getClass()获取对象所属的类,也可以通过new  ClassName()创建一个对象。这是因为在JVM中对象数据包含了一个指向方法区中对应类型信息的指针,可以通过该指针获取对应类信息。反过来JVM也可以根据方法区中类信息创建该类对象,甚至知道该类对象应该占用多少空间(但实际分配大小依赖于JVM实现)。同样通过class.getClassLoader()可以获取当前类加载器也是同样的原理。        还有个比较有意思的就是对象锁synchronized(obj),Java中每个对象都可以作为锁,同样也是因为对象本身包含了一个指向锁数据的指针,但由于绝大部分对象的锁都用不到,所以大部分JVM的实现,都只在第一个线程尝试获取对象锁时,才给该对象分配对应的锁数据。       关于堆空间本身的设计依赖具体的实现,下面是两种完全不同的可能设计,第一种把堆内存划分为句柄池和对象池两部分,对对象的引用指向句柄池,句柄池中每个元素又由两部分组成,一个指向方法区中的存放类数据的地址,一个指向对象池中的对象。这样的优势体现在,当垃圾回收器 回收内存并重新划分导致对象内存地址发生变化时,不需要更新所有引用的指向,而只需要更新句柄池中指向对象的指针,缺点就是中间需要额外经过一次查找。

第二种设计引用直接指向堆中的对象,这样的优缺点和第一种设计刚好相反,不需要额外的查找,但对象地址发生变化时,需要更新所有引用。

方法区:方法区在设计上也是所有线程共享,主要存储类相关信息(如字段/方法信息、常量池信息、对当前ClassLoader和Class的引用等等),在JVM加载某个类时,会抽取出对应.class文件中类相关的信息并以某种结构(依赖于JVM实现)存到方法区中,当程序运行时,JVM则会到方法区中去查找使用对应类信息(比如创建对象)。        值得注意的一点是由于所有线程共享方法区中的数据,所以方法区中数据的访问必须被设计成线程安全的,比如说多个线程并发加载同一类等等。另外方法区虽然也被称为“永久代”,但实际上其中的数据也是可以被垃圾回收器回收的,回收内容主要包括常量池中无用的常量、无用的类(具体判断依据请参考垃圾回收篇)。         虽然类信息具体的存储结构依赖于具体JVM实现,但为了提高方法的检索效率,部分JVM实现会为每个非抽象类生成一个方法表(方法表虽然加快了检索速度,但本身也会占用一定的内存空间,算是以空间换时间),方法表是一种数组结构,每个元素代表一个方法实例(从Java角度来说,每个元素就相当于一个Method对象)。这种情况下,对象不再直接指向方法区中的存放类信息的地址,而是指向方法表,通过方法表来间接关联对象与类型信息,从JVM的角度来看其基本指向如下图

Java栈与PC寄存器:JVM中每个线程都有自己的PC寄存器和Java栈,PC寄存器即计数器,表示指令在主存中的地址,每执行一条指令后PC+1,即指向下一条指令。Java栈代表线程在执行过程中的所有方法调用信息,JVM对栈只有压入栈桢和弹出栈桢两个操作,“栈”由"栈桢“组成,线程每调用一次方法JVM即会为它产生并压入一个“栈桢”,方法执行完毕即会弹出对应的栈桢,栈桢本质上就是一个内存片,用来存储方法局部变量和计算的中间结果,其中用于存储中间结果的部分又称为“操作数栈”,所以操作的数据即可能是“操作数栈”中的数据,也可能是“栈桢”中的数据。 值得注意的是局部变量和操作数栈的大小在编译时计算出来,并放置到class文件中,然后JVM就可以知道方法栈桢的大小,当调用一个方法时jvm将压入一个适当大小的栈桢至栈中,但栈在jvm中是有深度限制的,当线程调用的栈深度超出该限制时将抛出StockOverflowErro异常,如果jvm在扩展栈时无法获取更多内存则会抛出OutofMemoryErro。下面关于Java栈的图文描述及验证代码

public static void main(String[] args) throws InterruptedException {

//         testStackOverflowError();

testOutOfMemoryError();

}

public static void testStackOverflowError(){

testStackOverflowError();

}

public static void testOutOfMemoryError(){

byte[] bytes = new byte[2000000000];

}

本地方法栈:        本地方法栈即代表了线程在执行过程中调用本地方法的一系列信息,与Java栈不同的地方在于,本地方法并不受JVM的限制,对本地方法的调用不会导致JVM往Java栈中压入栈桢。关于本地方法与Java方法的调用可以简单的假设一下,假设某个线程在执行Java方法过程中调用了本地方法C1,且本地方法C1最终又调用了某Java方法,则在这个过程中JVM会先由Java栈进入本地方法栈最终又回到Java栈中,下图简单的描述了这种情况

执行引擎:        执行引擎负责字节码指令的执行,方法的字节码流由一系列有序指令组成,指令又由一个单字节的操作码 + 0个或多个操作数组成。操作码表示需要执行的操作,操作数表示操作的数据,一般来源于当前栈桢中的局部变量或当前Java栈桢中操作数栈的顶部,至于操作数的个数,由操作码决定(操作码本身就决定了它是否需要操作数,以及操作数的形式等等)。        不同的JVM中执行引擎也可能不同,最简单同时效率也最低的执行引擎是一次性解释字节码,它在每次运行方法时都把字节码翻译成本地代码再执行; 其次是即时编译(JITC),它在第一次执行方法时,会把对应的字节码翻译成本地机器代码并缓存,后续调用就可以重用缓存的本地机器代码;另外一种是自适应优化器(特殊的即时编译器), JVM一开始也会解释字节码,但它会监视程序的活动,并记录活动过程中使用最频繁的代码,然后把这些代码编译成本地代码,而其它代码则继续采用解释的方式。下面是javap -c com.alibaba.fastjson.JSONObject.class反汇编后的部分信息

public static java.lang.String valueToString(java.lang.Object) throws org.zend.sdklib.internal.utils.json.JSONException;

Code:

0: aload_0

1: ifnull        12

4: aload_0

5: aconst_null

6: invokevirtual #304                // Method java/lang/Object.equals:(Ljava/lang/Object;)Z

9: ifeq          16

12: ldc_w         #261                // String null

15: areturn

16: aload_0

17: instanceof    #635                // class org/zend/sdklib/internal/utils/json/JSONString

20: ifeq          83

23: aload_0

24: checkcast     #635                // class org/zend/sdklib/internal/utils/json/JSONString

27: invokeinterface #637,  1          // InterfaceMethod org/zend/sdklib/internal/utils/json/JSONString.toJSONString:()Ljava/lang/String;

32: astore_1

33: goto          46

36: astore_2

37: new           #52                 // class org/zend/sdklib/internal/utils/json/JSONException

40: dup

41: aload_2

42: invokespecial #640                // Method org/zend/sdklib/internal/utils/json/JSONException."<init>":(Ljava/lang/Throwable;)V

45: athrow

46: aload_1

47: instanceof    #92                 // class java/lang/String

50: ifeq          58

53: aload_1

54: checkcast     #92                 // class java/lang/String

57: areturn

58: new           #52                 // class org/zend/sdklib/internal/utils/json/JSONException

61: dup

aload_0表示将第一个局部变量压入到当前操作数栈中,aload指令后跟着的操作数必须是对象引用,这里第一个局部变量也是引用,即参数Object。 ifnull 12表示弹出栈顶对象(即刚压入的参数obj),判断是否为null, 为null则跳转到偏移量为12的分支处,关于指令相关的更多信息这里就不说了

[转载] JVM(一):JVM体系结构详解相关推荐

  1. 【转载】spring framework体系结构详解

    作者:一个无语的人 链接:https://www.jianshu.com/p/5b0c96975164 来源:简书 Spring的大名如雷贯耳,但一直在用公司自己的一套框架只是了解没有怎么使用,最近项 ...

  2. JVM运行时区域详解

    转载自 JVM运行时区域详解 我们知道的JVM内存区域有:堆和栈,这是一种泛的分法,也是按运行时区域的一种分法,堆是所有线程共享的一块区域,而栈是线程隔离的,每个线程互不共享. 线程不共享区域 每个线 ...

  3. JVM之XX参数详解

    JVM的XX参数详解 目录 标准参数 X参数(了解) XX参数(重点) -Xms,-Xmx是X参数还是XX参数,如何解释? 1. 标准参数 指每个版本都不会大发生改变的参数 测试命令: 2. X参数( ...

  4. JVM垃圾清理机制详解 ✨ 每日积累

    JVM垃圾清理机制详解 jvm内存结构中有一块地方叫做堆内存,里面存放着我们应用创建的对象,但是我们堆内存有限,对象在运行的时候持续创建,jvm有垃圾清理机制来清理对象确保堆内存的可用空间. 清理流程 ...

  5. JVM之内存结构详解

    对于开发人员来说,如果不了解Java的JVM,那真的是很难写得一手好代码,很难查得一手好bug.同时,JVM也是面试环节的中重灾区.今天开始,<JVM详解>系列开启,带大家深入了解JVM相 ...

  6. JVM启动参数配置详解

    JVM启动参数配置详解 1. JDK8的JVM启动参数默认配置 2. JDK8的JVM启动参数说明 2.1 基本参数 2.2 G1相关参数 2.3 辅助信息 1. JDK8的JVM启动参数默认配置 - ...

  7. JVM之类加载阶段详解

    JVM之类加载阶段详解 类加载阶段总览 加载 获取二进制流 将字节流转换为运行时数据结构 堆中生成Class对象 特殊 连接 验证 准备 解析 名词解释 何时进行 解析哪些类型 初始化 类 接口 使用 ...

  8. java异常体系_JAVA异常体系结构详解

    一.什么是异常 异常:程序在运行过程中发生由于硬件设备问题.软件设计错误等导致的程序异常事件.(在Java等面向对象的编程语言中)异常本身是一个对象,产生异常就是产生了一个异常对象.      --百 ...

  9. [转载]用数据说话 Pytorch详解NLLLoss和CrossEntropyLoss

    [转载]用数据说话  Pytorch详解NLLLoss和CrossEntropyLoss https://www.cnblogs.com/jiading/p/11979391.html NLL_Los ...

  10. java异常体系结构详解

    java异常体系结构详解 参考文章: (1)java异常体系结构详解 (2)https://www.cnblogs.com/hainange/p/6334042.html 备忘一下.

最新文章

  1. adg oracle 架构_云化双活的架构演进,宁夏银行新核心搭载Oracle 19c投产上线
  2. Win7下用VS2010编译QGIS2.9.0
  3. hadoop生态搭建(3节点)-05.mysql配置_单节点
  4. hdu1316 大数
  5. Android代码实现新建文件夹,并将文件保存到新建的文件夹中
  6. hdu 4533(树状数组区间更新+单点查询)
  7. 服务器配置文件salt,saltstack 配置文件详解
  8. ExecutorService- Future - Java多线程编程
  9. SAP Commerce Cloud OAuth 实现介绍
  10. ICC2 常用快捷键
  11. SQL注入——基于报错的注入(五)
  12. JVM学习-G1回收器
  13. 3-1-Servlet技术
  14. android 例子源码_AOSP系列文章(一)-Android系统源码下载和编译
  15. macos 运行linux,MacOS 上运行shell
  16. angular ts 表格_Angular 2的表格控件
  17. 数学----第一个重要极限证明
  18. 铀球(235U)的临界半径计算(1d,S8)
  19. win7系统安装SQL Server 2005开发版步骤详解
  20. 西游记团队与Scrum团队中的角色关系映射

热门文章

  1. c iostream.源码_通达信指标公式源码精准买卖主图指标公式免费分享
  2. mysql 多实例 独立配置文件_三、安装配置多实例MYSQL5.6-多独立配置文件方法
  3. hystrix 全局熔断_跟我学Spring Cloud(Finchley版)14Feign使用Hystrix
  4. c 语言 timestamp,c中的时间戳,精度为毫秒
  5. linux dns及时添加,在ARM Linux上成功实现添加DNS库
  6. 【dfs】Election of Evil
  7. 零基础入门深度学习(5) - 循环神经网络
  8. Linux Shell获取系统资源使用百分比(CentOS)
  9. Palo Alto Networks漏洞防护扩展至云端
  10. Redis配置和常用命令