电子文档交易市场
安卓APP | ios版本
电子文档交易市场
安卓APP | ios版本

深入解析Java的运行时数据区

17页
  • 卖家[上传人]:Baige****0346
  • 文档编号:266122470
  • 上传时间:2022-03-14
  • 文档格式:DOCX
  • 文档大小:2.98MB
  • / 17 举报 版权申诉 马上下载
  • 文本预览
  • 下载提示
  • 常见问题
    • 1、深入解析Java的运行时数据区 【摘要】 Java程序运行的过程中,JVM会将其所管理的内存划分成若干个区域,统称为是运行时数据区。其中,一些线程间共享的区域,随着JVM的启动而创建,JVM的退出而销毁;另一些线程私有的区域,则随着线程的开始而创建,线程的结束而销毁。如图所示,运行时数据区由以下几个区域所组成:程序计数器、Java虚拟机栈、本地方法栈、方法区、堆。Java程序运行的过程中,JVM会将其所管理的内存划分成若干个区域,统称为是运行时数据区。其中,一些线程间共享的区域,随着JVM的启动而创建,JVM的退出而销毁;另一些线程私有的区域,则随着线程的开始而创建,线程的结束而销毁。如图所示,运行时数据区由以下几个区域所组成:程序计数器、Java虚拟机栈、本地方法栈、方法区、堆。运行时数据区根据JVM的运行模型,程序运行前,JVM会将程序编译后的字节码加载到内存中;程序运行时,字节码解析器会读取内存中的字节码,按照顺序将字节码的指令解析成固定的操作。在这过程中,程序计数器(Program Counter Register)保存当前线程正在执行的字节码地址。从字节码运行的原理来看,单线

      2、程模型下的程序计数器貌似可有可无,字节码解析器会按照顺序将字节码翻译成固定操作,即使遇到分支跳转,也无碍程序正确运行下去。然而,现实中的程序往往是通过多线程协作来完成一个任务的,CPU会为每个线程分配一定的时间片,一个线程在其时间片耗尽之后会挂起,直到它再次获得时间片后才会重新运行。为了确保程序正确运行,线程必须从挂起的地方重新执行。有了程序计数器,就可以保证在涉及线程上下文切换的情景下,程序依然能够正确无误地运行下去。因此,程序计数器是线程私有的,避免了线程之间的相互影响。JVM会为每个线程都分配一块非常小的内存空间用作程序计数器,这也是唯一一个Java虚拟机规范没有规定OutOfMemoryError的运行时区域。JVM会给每个线程都分配一个私有的内存空间,称为Java虚拟机栈(Java VM Stack)。Java虚拟机栈随着线程的创建而创建,它与传统语言(如C语言)的栈有着类似的作用,JVM只会对其执行两种操作:栈帧(Stack Frame)的入栈和出栈。也就是说,Java虚拟机栈是存储栈帧的后进先出队列(LIFO)。每个方法的执行过程,都会伴随着栈帧的创建、入栈和出栈。栈帧是

      3、用来存储局部数据和部分过程结果的数据结构,主要包含局部变量表(Local Variable Table)、操作数栈(Operand Stack)、指向当前方法所属类的运行时常量池的引用(Runtime Constant Pool Reference),如图所示为Java虚拟机栈的模型。Java 虚拟栈模型局部变量表(LVT)局部变量表(LVT) 是一个索引以0开始的字节数组,存储了一个方法的所有入参和局部变量。LVT所存储的类型都是编译期可知的,包括各基础类型(byte、char、short、int、long、float、double、boolean)、对象引用(reference类型)和returnAddress类型(指向一条字节码指令的地址)。LVT有如下几个特点:1、第0个Slot(槽位)固定存储指向方法所属对象的this指针。2、除了long和double占用了连续2个Slot之外,其他类型都只占用了1个Slot。3、LVT按照变量的声明顺序进行存储。考虑以下代码来验证LVT的这些特点:先用javac命令将其编译成class文件:javac-gJvmStackLvt.java#

      4、javac编译.java文件,输出.class文件再使用javap命令解析class文件,就可以看到showLvt函数的LVT。从javap的输出结果可以看出,LVT的第0个Slot的名字为this,签名为Lcom/yrunz/jdk/chapter1/JvmStackLvt表示是指向JvmStackLvt类型的this指针,验证了特点1;变量l和d所在Slot的索引与其相邻的变量所在Slot 的索引相差2,表示l和d占用了2个Slot,而其他的变量都只占用了1个Slot,验证了特点2;LVT中变量的存储顺序也是与其声明顺序相同,验证了特点3。操作数栈(OS)操作数栈(OS) 用于在方法运算过程存储其中间的运算结果、方法入参和返回结果,它是一个后进先出(Last-In-First-Out,LIFO)的队列。JVM提供了对OS出栈和入栈的指令,如load指令属于入栈指令、store指令属于出栈指令。考虑以下代码:使用javac命令将其编译成class文件后,用javap解析得到:因为javap的输出结果中并不涉及操作数栈的内容,我们可以根据指令码和LVT来推断出OS的入栈和出栈过程,如图

      5、所示。运行时常量池引用(Runtime Constant Pool Reference)每个栈帧内都包含一个指向当前方法所属类的运行时常量池引用,也称为符号引用(Symbolic Reference),用于在类加载阶段对代码进行动态链接。动态链接所做的就是根据符号引用所表示名字,转换成对方法或变量的实际引用,从而实现运行时绑定(Late Binding)。考虑以下代码:使用javac命令将其编译成class文件后,用javap解析得到:可以看出,f2()的指令码中,有两处使用了符号引用。getfield指令引用的符号为#2,最终解析成com/yrunz/jdk/chapter1/JvmStackCpr.a:I;invokevirtual指令引用的符号为#3,最终解析成com/yrunz/jdk/chapter1/JvmStackCpr.f1:()V。Java虚拟机规范规定,Java虚拟机栈可以被实现成固定大小,也可以实现成可动态地扩展和收缩。JVM通常会提供设定Java虚拟机栈容量大小或范围的参数,比如-Xss参数用于设定栈的大小。当线程请求分配的栈空间超过JVM指定的最大容量时,程序

      6、就会抛出StackOverflowError异常;而对于栈空间可动态扩展的情形,当尝试扩展却无法分配到足够内存时,程序就会抛出OutOfMemoryError异常。本地方法栈(Native Method Stack) 的作用与Java虚拟机栈类似,区别在于后者是为Java方法服务,而本地方法栈则为native方法服务。Java虚拟机规范没有对native方法机制及其实现语言做强制规定,如果JVM不提供native方法,则无需实现本地方法栈。本地方法栈既可以被实现成固定大小,也可以实现成可动态地扩展和收缩,因此在特定的场景下也会抛出StackOverflowError异常和OutOfMemoryError异常。方法区(Method Area)是线程间共享的区域,在JVM启动时创建,用于存储类的元信息、静态变量、常量、普通方法的字节码等内容。方法区可以被实现成大小固定或可动态扩展和收缩,如果内存空间不满足内存分配要求就会抛出OutOfMemoryError异常。对于HotSpot虚拟机而言,在JDK 1.8以前,方法区被实现为“永久代”(Permanent Generation),属于堆的

      7、逻辑组成部分,并提供了两个参数调节其大小,-XX:PermSize用于设定初始容量,-XX:MaxPermSize用于设定最大容量。JDK 1.8之后,HotSpot不再有“永久代”的概念,类的元信息数据迁移到被称为“元空间”(Metaspace)的新区域,而静态变量、常量等则存储于堆中。元空间没有使用堆内存,而是分配在本地内存中,默认情况下其容量只受可用的本地内存大小限制。类似地,HotShot虚拟机也提供了两个参数来调节其大小,-XX:MetaspaceSize用于设定初始容量,-XX:MaxMetaspaceSize用于设定最大容量。运行时常量池(Runtime Constant Pool)运行时常量池(Runtime Constant Pool)属于方法区的一部分,class文件被加载到内存后,其中的常量池信息(包括符号引用和编译期可知的字面值常量)就被存储于此。 这些信息可通过javap解析class文件查看,考虑如下代码:使用javac命令将其编译成class文件后,用javap解析得到:由结果可知,class常量池信息中包含了MethodAreaRtcp类所有的符号引用和

      8、编译期可知的字面值常量。什么样的字面值才算是字面值常量?1、字符串字面值。编译器在编译 java 代码时,会将字符串字面值添加到常量池中,比如例子中的hello和 world在常量池中的位置分别为#67和#83。2、用final修饰的基础类型成员变量的字面值。注意,必须是用final修饰的,而且必须是类的成员变量的字面值才会被编译器添加到常量池中,如i2的值2在常量池中的位置为#32。i5虽然是用final修饰的基础类型成员变量,但是它的值5是运行时函数调用的结果,因此并未出现在常量池中;i7虽然是被final修饰,但是它本身是一个局部变量,因此它的值7并未出现在常量池中。另外,short、byte和char类型的字面值常量会被编译器转换为int类型后存入常量池中,如例子中s1、b1和c1的字面值常量对应在常量池中的位置分别为#41、#44和#47,它们的类型都是Integer。3、由字面值常量相加得到的结果。因为编译器做了优化,多个字面值常量相加后得到的结果,也会被添加到常量池中。如例子中i6和str3,在常量池中对应的位置为#38和#85。程序执行阶段也会有新的常量加入运行时常量池

      9、中。在Java程序执行阶段,也会有新的常量加入到运行时常量池中,这部分并不属于class常量池,因此我们无法从javap的结果中找到这些常量。1、String.intern()方法的返回值会加入运行时常量池中。String.intern()方法调用时,如果常量池中已经存在与其相等的String对象(使用equals比较时返回true),则返回该字符串;否则将该字符串添加到常量池中,然后将其返回。如下例子验证了这一特点:2、基础类型的包装类也用到了常量池技术。Java的部分基础类型的包装类(Character、Byte、Short、Integer、Long、Boolean)也用到了常量池技术,当使用数值字面值给它们赋值时,它们就会被存储到运行时常量池中。此外,位于运行时常量池中的包装类相加得到的结果,也会被存储在常量池中。值得注意的是,只有在 -127, 127 的范围内,包装类才会使用到常量池技术,超过该范围的还是会在堆中存储。以下例子验证了这些特点:堆(Heap)是运行时数据区中最大的一块区域,绝大部分的对象(包括类实例和数组)都在上面存储。堆是所有线程共享的,随着JVM的启动而创建。我们通过new创建出来的对象都分配于此,而且无需主动释放对象内存,统一由垃圾收集器(Garbage Collector,GC)来进行管理和销毁 这也是Java跟C+相比区别最大的特点之一。当堆中没有足够的内存来创建对象时,就会抛出OutOfMemoryError异常。堆的分代管理JVM对堆进行了分代管理,分成新生代(Young Generation)和老年代(Old Generation),其中新生代中又分为Eden Space、From Survivor Space、To Survivor Space三个区域,如图所示。JVM将堆划分成这么多的区域,主要是为了方便垃圾收集器对对象进行管理,现代的垃圾收集器一般都采用了分代收集

      《深入解析Java的运行时数据区》由会员Baige****0346分享,可在线阅读,更多相关《深入解析Java的运行时数据区》请在金锄头文库上搜索。

      点击阅读更多内容
    最新标签
    监控施工 信息化课堂中的合作学习结业作业七年级语文 发车时刻表 长途客运 入党志愿书填写模板精品 庆祝建党101周年多体裁诗歌朗诵素材汇编10篇唯一微庆祝 智能家居系统本科论文 心得感悟 雁楠中学 20230513224122 2022 公安主题党日 部编版四年级第三单元综合性学习课件 机关事务中心2022年全面依法治区工作总结及来年工作安排 入党积极分子自我推荐 世界水日ppt 关于构建更高水平的全民健身公共服务体系的意见 空气单元分析 哈里德课件 2022年乡村振兴驻村工作计划 空气教材分析 五年级下册科学教材分析 退役军人事务局季度工作总结 集装箱房合同 2021年财务报表 2022年继续教育公需课 2022年公需课 2022年日历每月一张 名词性从句在写作中的应用 局域网技术与局域网组建 施工网格 薪资体系 运维实施方案 硫酸安全技术 柔韧训练 既有居住建筑节能改造技术规程 建筑工地疫情防控 大型工程技术风险 磷酸二氢钾 2022年小学三年级语文下册教学总结例文 少儿美术-小花 2022年环保倡议书模板六篇 2022年监理辞职报告精选 2022年畅想未来记叙文精品 企业信息化建设与管理课程实验指导书范本 草房子读后感-第1篇 小数乘整数教学PPT课件人教版五年级数学上册 2022年教师个人工作计划范本-工作计划 国学小名士经典诵读电视大赛观后感诵读经典传承美德 医疗质量管理制度 2
    关于金锄头网 - 版权申诉 - 免责声明 - 诚邀英才 - 联系我们
    手机版 | 川公网安备 51140202000112号 | 经营许可证(蜀ICP备13022795号)
    ©2008-2016 by Sichuan Goldhoe Inc. All Rights Reserved.