当前位置: 首页 > Web前端 > HTML

jvm中的数据结构:Java虚拟机规范运行时数据区

时间:2023-03-28 13:20:10 HTML

虽然官网下载的jdk自带了java虚拟机,但是java语言规范并没有规定jvm的实现。在网上查了jvm的资料,几乎没有关于jvm实现和jvm规范异同的说明。jvm规范中的大部分内存结构都与HotSpotjvm中实现的java堆中对象的生命周期/内存模型混合在一起。因此,在了解jvm的过程中,已知以下两个问题困扰着我:1.java程序的内存模型在不同虚拟机之间是否相同?2、不同虚拟机接受的java参数是否相同?是否可以在其他jvm中使用默认的hotspot虚拟机-XX系列参数?网上各种教程对上述??问题含糊不清。这里我要推荐周志明老师的《深入理解Java虚拟机:JVM高级特性与最佳实践》,里面详细解释了jvm中的上述问题和各种细节。以下内容是我在对比了周志明老师的《深入理解Java虚拟机:JVM高级特性与最佳实践》和官方的《JVM规范》之后,对《JVM规范》RuntimeDataAreas一章的翻译,并加入了我个人的整理和理解。注:本来想在《JVM规范》的内存结构章节梳理一下JVM内存结构的发展,但是对比了javase6和javase17的相关标准后发现,《JVM规范》并没有太大的进步关于jvm内存结构的变化,所以本文内容基于jdk17JVM内存模型概述jvm会将内存划分为若干区域进行管理,这些区域有各自的用途和生命周期。根据各个数据区的作用,这些区域可以分为两类,即线程间共享的数据区和线程间私有的数据区,其中方法区和堆是所有线程共享的数据区,虚拟机栈,本地方法栈和程序计数器是每个线程私有的。程序计数器(ProgramCounterRegister)PCRegister是一个小空间,用来表示当前线程正在执行的指令所在的位置。在jvm中,字节码解释器需要改变PC寄存器的值来选择下一条要执行的字节码指令。程序中的分支、循环、跳转、异常处理、线程回收等基本功能都需要依赖这个计数器来完成。当jvm执行一个类方法时,PCRegister用于指示当前指令所在的位置。与PCRegister一样,jvm堆栈对每个线程都是私有的。它的生命周期与线程相同。它在线程创建时创建,线程结束时销毁。jvm栈的作用和其他语言一样,用来存放局部变量表、操作数栈、动态链接、方法出口等。java程序每次调用一个方法,都会有一个栈帧(Frame)会生成,每一帧都会有自己的局部变量表、操作数栈、动态链接、方法出口等信息。jvm栈中只有两个动作:framestacking和framepopping。栈不直接操作frames,所以frames可以在heap上分配,jvm的stack可以是不连续的内存段,固定大小,也可以动态分配。栈帧由栈上的线程创建,不能被其他方法引用。它在方法结束或中断后被调用并销毁时产生。每个帧都有自己的局部变量表、操作数栈、常量池、动态链接、方法出口等信息。但是帧数据结构和帧大小取决于具体的jvm实现。一个线程,同时只有一帧在执行,这一帧称为当前帧,该帧对应的方法称为当前方法,当前方法所在的类称为当前类。当当前方法调用其他方法或当前方法结束时,当前框架停止工作。局部变量表局部标量表用于存储执行当前方法所需的所有变量,因此局部变量表是在编译时确定的。局部变量表有十种数据类型:单字节局部变量(单个局部变量):boolean、byte、char、short、int、float、reference、returnAddress双字节局部变量(一对局部变量):长帧和双帧使用索引定位局部变量,双字节变量使用低位作为索引。在局部变量表中,索引为零,值固定为这个。操作数栈操作数栈用于方法中的计算,遵循后进先出的原则,操作数栈的大小在编译时确定。本机方法栈与jvm栈非常相似。不同的是,虚拟机栈是为虚拟机执行Java方法(即字节码)服务的,而本地方法栈是虚拟机使用的本地(Native)栈。)方法服务。本机方法堆栈可以是固定大小或动态分配的。JAVA堆在java程序中,java堆是所有线程共享的,用于在运行时为所有类实体和数组分配内存。堆中的内存由GC管理,jvm并没有指定GC,GC的具体实现由jvm自己选择。堆可以是固定大小的,也可以是动态分配的,堆空间可以是一段不连续的内存,但逻辑上应该认为是连续的。方法区也是各个线程共享的内存区,用于存放已经被虚拟机加载过的类类型信息、常量、静态变量、编译器编译后的代码缓存等数据。方法区在逻辑上是堆的一部分,也就是说这部分区域也可以被垃圾回收,但是jvm规范规定简单的jvm实现在垃圾清理时可以选择跳过这部分区域。方法区的具体位置以及如何管理编译后的代码由具体的jvm决定,这与java堆和jvm栈是一样的。方法区可以是固定大小的,也可以是动态分配的。方法区可以是物理上不连续的内存碎片,但应该被视为逻辑上连续的,并允许用户控制这些区域的初始大小、最大大小和最小大小配置。常量池(Runtimeconstantpool)是方法区的一部分。Class文件中除了类的版本、字段、方法、接口等描述信息外,还有一个常量池表,用于存放编译过程中产生的各种字面量。还有符号引用,这部分会在类加载后存放在方法区的常量池中。StackOverflowError当jvm栈是固定大小时,但是线程需要更多栈空间,抛出StackOverflowError当本地方法栈是固定大小时,但是线程需要更多栈空间,在堆、栈、方法区抛出StackOverflowErrorOutOfMemoryErrorjava程序、常量池、本地方法栈需要扩展内存空间,当jvm剩余内存空间不足时,抛出OutOfMemoryError。创建线程时,当jvm的内存空间不足时,会抛出OutOfMemoryError。类库java.lang.reflect类类加载和创建类或接口,如ClassLoader类或接口链接初始化安全类库java.security和其他类,如SecurityManager多线程弱引用,如java.lang.ref经常在java堆空间结构中会出现“新生代”、“老年代”、“永久代”等概念,但是这些区域划分只是java默认的虚拟机热点和一些jvm的特性,《JVM规范》确实不做更详细的堆很多资料对此含糊不清,甚至写出“jvm堆内存分为新生代、老年代、永久代……”等误导性言论。同时,每个java规范中只提供了简单且有限的java标准参数:[ThejavaCommand]和jvm相关的参数取决于具体的jvm实现,在不同的虚拟机中并不通用。欢迎关注我的公众号:打码的老贾,回复“领取”送《Java面试》素材,阿里、腾讯、字节、美团、饿了么等大厂