1.什么是JVM的跨平台
JVM的跨平台性是指在不同操作系统和硬件平台上都能运行相同的Java程序,而无需对程序进行修改(一次编译,到处运行)。
实现跨平台的方式主要依赖于它的工作原理。以下是详细步骤:
-
编译源代码:Java源代码文件(.java)被编译器编译成字节码文件(.class文件)。
-
跨平台的字节码:这些字节码文件非常特殊,它们不像其他编译语言的输出那样是特定于某种硬件的机器代码,而是一种中间代码。这使得它们可以在任何运行JVM的平台上执行。
-
JVM的解释和执行:在目标机器上,安装有JVM的环境会解释或编译执行这些字节码。在JVM内部,它可以解释字节码,或者它可以将字节码进一步编译成机器代码来提高性能。因为JVM是与平台相关的,所以它知道如何将字节码翻译成特定平台的机器代码。
这就是JVM如何实现跨平台的方式。核心思想是Intel(或任何其他CPU)并不直接执行Java字节码,而是由JVM自己来解释和执行。这相当于为所有的平台建立了一个抽象的、统一的运行环境,使得字节码“认为”自己总是在同一个平台上运行。通过这种方式,Java程序可以在任何设备上运行,只要该设备安装了一个JVM。
2.JVM组成
JVM由四部分组成:类加载子系统、运行时数据区、执行引擎、本地方法接口
1. 类加载子系统:
通过类加载机制加载类的class文件,如果该类是第一次加载 (多次使用同一个类,该类已经被加载过,就不会走后面的流程了),会执行加载、验证、解析。只负责class文件的加载,至于是否可运行,则由执行引擎决定。
类加载过程是在类加载子系统完成的:加载 --> 链接(验证 --> 准备 --> 解析) --> 初始化
2. 运行时数据区:
在程序运行时,存储程序的内容(例如字节码、对象、参数、返回值等)。运行时数据区包括本地方法栈、虚拟机栈、方法区、堆、程序计数器。只有方法区和堆是各线程共享的进程内存区域,其他运行区都是每个线程可以独立拥有的。
- 本地方法栈(线程私有):存放本地方法调用过程中的栈帧。用于管理本地方法(native 方法)的调用,本地方法是C语言写的。不是所有虚拟机都支持本地方法栈,例如Hotspot虚拟机就是将本地方法栈和虚拟机栈合二为一。栈解决程序的运行问题,即程序如何执行、如何处理数据。
- 虚拟机栈(线程私有):存放Java方法调用过程中的栈帧。用于管理Java方法的调用,Java方法是开发时写的Java方法。
- 栈帧:栈帧是栈的元素,由三部分组成,即
局部变量表(存方法参数和局部变量(对象引用及基本数据类型))、
(注意:对象引用 ( int类型 ) 和基本数据类型都是值类型,因此直接存储到内存中,而不是存地址再去寻址!)
操作数栈(存方法执行过程中的中间结果,或者其他暂存数据)和
帧数据区(存方法返回地址、线程引用等附加信息)。
- 栈帧:栈帧是栈的元素,由三部分组成,即
- 方法区(线程共享):方法区和Java堆一样(但是方法区是
非堆
),是各个线程共享的内存区域,是用于存储已经被JVM加载的类信息、常量、静态变量、即时编译器编译后的代码等数据。对于JDK1.8时,JVM模型进行了改造,将元数据放到了本地内存,将常量池和静态变量放到了Java堆中。- 方法区/永久代和元空间的关系:方法区是一个抽象概念,永久代和元空间是方法区的实现方式。
- 永久代/方法区(jdk1.7之前):属于JVM方法区的内存,用来存储类的元数据,如类名、方法信息、字段信息等一些静态的数据。JDK7及之前方法区也叫永久代。缺点是内存大小固定,容易出现oom问题。可以通过-XX:PermSize设置永久代大小。永久代对象只能通过Major GC(又称Full GC)进行垃圾回收。
- 元空间(jdk1.8之后):是Hotspot在JDK8引入的,用于取代永久代。
元空间用于存储:类的元数据信息(如类名,访问修饰符,常量池,字段和方法数据等)
元空间属于本地内存,由操作系统直接管理,不再受JVM管理。同时内存空间可以自动扩容,避免内存溢出。默认情况下元空间可以无限使用本地内存,也可以通过-XX:MetaspaceSize限制内存大小。 - 实例变量:无论是 JDK 1.7 还是 JDK 1.8,实例变量始终存储在堆内存中。
静态变量:在 JDK 1.7 中,静态变量存储在方法区(永久代)中。在 JDK 1.8 中,静态变量存储在元空间中,但元空间使用的是本地内存。
- 方法区/永久代和元空间的关系:方法区是一个抽象概念,永久代和元空间是方法区的实现方式。
- 堆&#x