乐趣区

关于前端:jvm中的数据结构Java-Virtual-Machine-Specification-Runtime-Data-Areas

官网下载的 jdk 尽管自带 java 虚拟机,然而 java 语言标准并没有指定 jvm 实现,查阅了网络上对于 jvm 的材料,简直没有对于 jvm 实现与 jvm 标准之间的异同点进行别离阐明,大都将 jvm 标准中的内存构造与 HotSpot jvm 中实现的 java 堆中对象的生命周期混 / 内存模型为一谈,因而在了解 jvm 的过程中,以下两个问题已知困扰着我:

1、不同的虚拟机之间,java 程序的内存模型一样吗?

2、不同的虚拟机之间承受的 java 参数是否雷同?默认的 hotspot 虚拟机 -XX 系列参数在其它的 jvm 中是否也可能应用?

网上的各种教程对上述问题则是语焉不详,在这里要举荐一下周志明老师的《深刻了解 Java 虚拟机:JVM 高级个性与最佳实际》,数中具体解释了上述问题以及 jvm 中的各种细节。

以下内容是我比照周志明老师的《深刻了解 Java 虚拟机:JVM 高级个性与最佳实际》与官网的《JVM 标准》中的内容后,对《JVM 标准》Runtime Data Areas 章节的翻译,并退出了集体的整顿与了解。

注:原本想就《JVM 标准》中的内存构造章节整顿出 JVM 内存构造倒退详情,然而在比照了 java se6 至 java se17 的相干规范后,发现《JVM 标准》并没有对 jvm 内存构造做出过大的变动,因而本文内容是基于 jdk17

JVM 内存模型概述

jvm 会将内存划分为若干个区域进行治理,这些区域有各自的用处以及生命周期。依据各个数据区的作用,能够将这些区域分为两大类,即线程间共享的数据区和线程间公有的数据区,其中办法区和堆是由所有线程共享的数据区域,虚拟机栈、本地办法栈和程序计数器为各个线程公有。

程序计数器(Program Counter Register)

PC Register 是一块较小的空间,用于批示以后线程正在执行的指令的地位。在 jvm 中,字节码解释器的须要扭转 PC register 的值来选取下一条须要执行的字节码指令,程序中的分支、循环、跳转、异样解决、线程回复等根底性能都须要依赖这个计数器来实现。当 jvm 执行类办法时,PC Register 用于批示以后指令的地位。

jvm 栈与 PC Register 一样是各个线程公有的,它的生命周期与线程一样,在线程创立时被创立,在线程完结时被销毁。jvm 栈的性能与其它语言雷同,用于贮存局部变量表、操作数栈、动静链接、办法进口等。java 程序每调用一个办法,就会产生一个栈帧(Frame),每个帧中,都会有各自的局部变量表、操作数栈、动静链接、办法进口等信息。

jvm 栈中仅有两个动作:帧入栈及帧出栈,栈不间接操作帧,所以帧是能够调配 在堆上,且 jvm 栈能够是不间断的内存片段,能够是固定大小,也能够动态分配。

栈帧由线程在栈中创立,且不能被其它办法援用,在办法调用时产生,在办法完结或者中断后销毁。每个帧都有本人的局部变量表、操作数栈、常量池、动静链接、办法进口等信息。然而帧的数据结构以及帧大小取决于具体的 jvm 实现。一个线程,同时仅有一个帧在执行,这个帧被称为 current frame,帧对应的办法被称为 current method,以后办法所在的类称为 current class。以后办法调用其它办法或者以后办法完结时,current frame 进行工作。

局部变量表

部分标量表用于贮存执行以后办法所须要的所有变量,因而局部变量表在编译期决定。局部变量表中共有十种数据类型:

单字节局部变量(a single local variable):boolean、byte、char、short、int、float、reference、returnAddress

双字节局部变量(a pair of local variables):long、double

帧中采取索引的形式定位局部变量,双字节变量采纳低位作为索引。在局部变量表中,索引为零的地位,值固定为 this。

操作数栈

操作数栈用于执行办法中的计算,遵循后进先出准则,且操作数栈的大小在编译期决定。

本地办法栈

本地办法栈与 jvm 栈的作用十分类似,其区别只是虚拟机栈为虚拟机执行 Java 办法(也就是字节码)服务,而本地办法栈则是为虚拟机应用到的本地(Native)办法服务。本地办法栈能够是固定大小,也能够动态分配。

JAVA 堆

一个 java 程序中,java 堆由所有线程共享,用于运行时给所有的类实体和数组分配内存。堆中的内存由 GC 治理,jvm 也不指定 GC,GC 的具体实现由 jvm 本人抉择。堆能够是固定大小也能够动态分配,堆空间能够是不间断的内存片段,但在逻辑上它应该被视为间断的。

办法区也是由各个线程共享的内存区域,用于贮存已被虚拟机加载的类类型信息、常量、动态变量、即便编译器编译后的代码缓存等数据。办法区在逻辑上是堆的一部分,这意味着这一部分区域也是能够被进行垃圾回收的,然而 jvm 标准规定,简略的 jvm 实现能够抉择在垃圾清理时跳过该区域。

办法区的具体位置,以及如何治理编译后的代码,由具体的 jvm 决定,与 java 堆和 jvm 栈雷同,办法区能够是固定大小也能够动态分配,办法区在物理上能够是不间断的内存片段,然而在逻辑上该当被视为间断的,且容许用户管制这些区域的初始大小、最大大小以及最小大小等配置。

常量池 (Runtime constant pool) 是办法区的一部分,Class 文件中除了有类的版本、字段、办法、接口等形容信息外,还有一个常量池表,用于寄存编译期生成的各种字面量与符号援用,这部分内容将在类加载后寄存到办法区的常量池中。

StackOverflowError

当 jvm 栈为固定大小,而线程须要更大的栈空间时,抛出 StackOverflowError

当本地办法栈为固定大小,而线程须要更大的栈空间时,抛出 StackOverflowError

OutOfMemoryError

java 程序中堆、栈、办法区、常量池、本地办法栈须要扩充内存空间,而 jvm 残余的内存空间有余时,抛出 OutOfMemoryError

创立线程时,jvm 的内存空间有余时,抛出 OutOfMemoryError

须要 jvm 非凡反对的类库(局部)

  • 反射类类库 java.lang.reflect
  • 类 Class
  • 加载和创立类或接口,例 ClassLoader
  • 类或接口链接的初始化
  • 安全性类库 java.security 和其它类如 SecurityManafer
  • 多线程
  • 弱援用,例 java.lang.ref

java 堆中常常会呈现“新生代”、“老年代”、“永恒代”等空间结构概念,然而这些区域划分仅仅是 java 的默认虚拟机 hotspot 以及局部 jvm 的个性,《JVM 标准》中并没有对堆进行更粗疏的划分,不少材料上都对此语焉不详,甚至写着“jvm 堆内存分为新生代、老年代、永恒代…”等容易让人误会的说法。

同时各 java 标准中仅提供简略且无限的 java 规范参数:[The java Command]而与 jvm 相干的参数更是依赖具体的 jvm 实现,在不同的虚拟机中并不通用。
欢送关注我的公众号:敲代码的老贾,回复“支付”赠送《Java 面试》材料,阿里,腾讯,字节,美团,饿了么等大厂

退出移动版