官网下载的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面试》材料,阿里,腾讯,字节,美团,饿了么等大厂