JVM定义
JVM是Java Virtual Machine(Java虚拟机)的缩写,JVM是一种用于计算设备的规范,它是一个虚构出来的计算机,是通过在实际的计算机上仿真模拟各种计算机功能来实现的。
引入Java语言虚拟机后,Java语言在不同平台上运行时不需要重新编译。Java语言使用 Java虚拟机屏蔽了与具体平台相关的信息,使得Java语言编译程序只需生成在Java虚拟机上运行的目标代码(字节码),就可以在多种平台上不加修改地运行。
2 Java跨平台与JVM的联系
JVM是一个软件,在不同的平台上,JVM有不同的版本。
Java在编译之后会生成一种.class文件,这种文件称为字节码文件。
JVM虚拟机就是将Java编译后的.class文件翻译成特定平台下的机器码,然后运行。也就是说,在不同平台上装上平台对应的JVM虚拟机后,就可以将Java字节码文件转换,然后运行我们的Java程序。
值得注意的是,Java编译后的结果是生成字节码,而不是机器码。字节码是不可以直接运行的,必须通过JVM再次翻译成机器码才可以运行。即使是将Java程序打包成可执行文件,也仍然需要JVM的支持才可以运行。 跨平台的是Java程序,而不是JVM。
JVM是用C/C++开发的,不能平台,不同的平台下JVM的版本是不同的。
(备注: 机器码(machine code),学名机器语言指令,有时也被称为原生码(Native Code),是电脑的CPU可直接解读的数据。)
JVM体系结构概述
JVM启动后,对操作系统来说,JVM是一个的进程,这个进程基本结构包括内容如下
类装载器(ClassLoader)
负责加载class文件,class文件在文件开头有特定的文件标示,并且ClassLoader只负责class文件的加载,至于它是否可以运行,则由Execution Engine决定
执行引擎(Execution Engine)
执行引擎负责解释命令,提交操作系统执行。
本地接口(Native Interface)
Java语言本身不能对操作系统底层进行访问和操作,但是可以通过JNI接口调用其他语言来实现对底层的访问。 主要是调用c或c++实现的本地接口
本地接口的作用是融合不同的编程语言为Java所用,它的初衷是融合 C/C++程序,Java诞生的时候是C/C++横行的时候,要想立足,必须有调用C/C++程序,于是就在内存中专门开辟了一块区域处理标记为Native的代码,它的具体做法是Native Method Stack中登记Native方法,在Execution Engine 执行时加载Native libraries。
目前该方法使用的越来越少了,除非是与硬件有关的应用,比如通过Java程序驱动打印机或者Java系统管理生产设备,在企业级应用中已经比较少见。因为现在的异构领域间的通信很发达,比如可以使用Socket通信,也可以使用WebService等等,不多做介绍。
运行时数据区(Runtime Data Area)
执行引擎执行过程中产生的数据会存储在一块内存区域。这块内存区域就是运行时数据区。
JVM内存区域
从图中可以看出jvm的内存区域可以按照 线程共享【堆、方法区】和 线程私有【虚拟机栈、本地方法栈、程序计数器】进行划分。
下面示例 -》对应的原理结构图
线程私有
Java虚拟机栈
描述:
- 线程执行了一个方法,就会对这个方法调用创建对应的一个栈帧
- 栈帧中存储了 方法的局部变量表、操作数栈、动态链接、方法出口(方法的返回地址)等信息。
- 局部变量表
- 用于存储方法参数和方法内部定义的局部变量。
- 操作数栈
- 可以理解为PC寄存器:用于作为计算过程的临时数据存储区。
- 临时数据指的变量对应的具体值 eg:int a=1; 对应的是1这个值
- CPU执行load命令时能将数据加载到操作数栈。
- 可以理解为PC寄存器:用于作为计算过程的临时数据存储区。
- 动态链接:是在程序运行期间完成的将符号引用替换为直接引用
- 方法返回地址
- 方法退出之后都需要返回方法被调用的位置。
特点:
- 方法退出之后都需要返回方法被调用的位置。
- 局部变量表
- 栈的生命周期(线程生命周期一致)
- 不存在垃圾回收的问题
- 可能的异常信息( java.lang.StackOverflowError)
本地方法栈
跟Java虚拟机栈类似的,也是存放各种native方法的局部变量表之类的信息。
程序计数器
描述:
- 每个线程都有一个程序计数器
- 用来记录当前执行的字节码指令的位置的(行到了哪一条字节码指令)
线程共享
方法区 -》永久代(Meta Space) | 元数据空间
描述:
- JVM规范层面的东西
- jdk1.7 叫做方法区(永久带permGenspace) ;JDK 1.8 + 叫做 方法区( "Metaspace"(元数据空间))
- 永久带或metaspace是对方法区的不同实现,是实现层面的东西。
功能: - 存储被jvm加载的类的结构信息、常量、静态变量、编译器编译后的代码等
- 类结构信息:类的名称、方法、字段等信息。
class文件会被Java虚拟机加载到哪个位置存储-chatgpt
在传统的Java虚拟机中,.class
文件在加载时会被存储在永久代(Permanent Generation)中。永久代是Java虚拟机的一部分,用于存储类的元数据信息,包括类的结构、方法、字段等信息。这意味着在传统的Java虚拟机中,加载的类信息(包括字节码)将占用永久代的空间。
然而,从Java 8开始,永久代被移除,取而代之的是元空间(Metaspace)。在元空间中,类的元数据信息将被动态地分配和管理内存,而不再受到永久代的限制。因此,.class
文件的内容会被加载到元空间中。
总体而言,具体的存储位置取决于Java虚拟机的实现版本,而在现代的Java虚拟机中,元空间是类元数据信息的存储区域。
Java堆
描述:
Java进程启动时内存分配: 当通过Java命令启动Java进程时,Java虚拟机会分配一块内存,这块内存包括了不同的区域,如堆空间、方法区(在Java 8之前,现在是元空间)、虚拟机栈、本地方法栈等。
内存的一部分用于创建堆空间,当程序中创建对象的时候,就从堆空间中分配内存。 (扩展:GC 是 JVM 内部的一个进 程,回收无效对象的内存用于将来的分配 )
存储 实例对象(所有的实例对象以及数组都要在堆上进行分配),
Java堆是垃圾收集器管理的主要区域因此很多时候也被称为 "GC堆"。
示例:
可以认为局部变量表里的"replicaManager"指向了Java堆内存里的ReplicaManager对象
4.3 完整流程
JVM内存参数设置
Spring Boot程序的JVM参数设置格式(Tomcat启动直接加在bin目录下catalina.sh文件里):
java ‐Xms2048M ‐Xmx2048M ‐Xmn1024M ‐Xss512K ‐XX:MetaspaceSize=256M ‐XX:MaxMetaspaceSize=256M ‐jar microservice‐eurek a‐server.jar
关于元空间的JVM参数有两个:
- -XX:MetaspaceSize=N和 -XX:MaxMetaspaceSize=N
- -XX:MaxMetaspaceSize: 设置元空间最大值, 默认是-1, 即不限制, 或者说只受限于本地内存大小。
- -XX:MetaspaceSize: 指定元空间触发Fullgc的初始阈值(元空间无固定初始大小), 以字节为单位,默认是21M,达到该值就会触发 full gc进行类型卸载, 同时收集器会对该值进行调整:(动态调整)
- 如果释放了大量的空间, 就适当降低该值; 如果释放了很少的空间, 那么在不超 过-XX:MaxMetaspaceSize(如果设置了的话) 的情况下, 适当提高该值。这个跟早期jdk版本的-XX:PermSize参数意思不一样,- XX:PermSize代表永久代的初始容量。
- 由于调整元空间的大小需要Full GC,这是非常昂贵的操作,如果应用在启动的时候发生大量Full GC,通常都是由于永久代或元空间发生 了大小调整,基于这种情况,
- 一般建议在JVM参数中将MetaspaceSize和MaxMetaspaceSize设置成一样的值,并设置得比初始值要大, 对于8G物理内存的机器来说,一般我会将这两个值都设置为256M。