一、JVM 简介
1. 什么是 JVM?
JVM(Java 虚拟机)是 Java 语言的核心组成部分,它是一个执行 Java 字节码的虚拟机。Java 程序必须在 JVM 上运行,使其与具体平台无关,JVM 独立于操作系统和硬件架构。
2. JVM 的作用和特点
JVM 的主要作用是将 Java 代码翻译成可执行的字节码,同时提供运行时的环境和支持库。JVM 采用了即时编译、垃圾回收、类加载、安全检查等特性,使得 Java 程序具有高效性、安全性和可移植性等特点。
3. JVM 的结构和运行原理
JVM 的结构包括类加载器、运行时数据区和执行引擎等部分。其中,类加载器负责加载字节码文件,运行时数据区负责存储程序运行时所需的数据,执行引擎则负责解释执行字节码。JVM 运行原理是:首先将 Java 代码编译成字节码文件,JVM 将字节码文件加载进内存,并解释执行字节码,最终输出程序运行结果。
二、JVM 运行时数据区
1. 概述
JVM 运行时数据区是 JVM 在运行时需要用到的内存区域。JVM 运行时数据区分为方法区、堆内存区、虚拟机栈、本地方法栈和程序计数器。
2. 方法区
方法区主要用于存放已经被 JVM 加载的类、接口、方法的字节码信息。
常量池
JVM 中常量池是方法区的一部分,主要用于存储类文件中常量信息的集合。常量池是类文件中重要的数据结构,其内部包含了多个不同类型的常量,如字符串、数字、类和接口的符号引用等。
类信息
类信息主要包括类的属性、方法和构造方法等信息。
字段信息
字段信息主要用于描述类、接口、枚举和注解类型的属性。
方法信息
方法信息用于描述类或接口的方法,包括方法名称、参数数量、返回值类型等。
3. 堆内存区
堆内存区主要用于存放 Java 对象。JVM 运行时会在堆内存区创建对象实例,并在空闲时进行垃圾回收。
对象的创建和销毁
在 Java 程序中,对象的创建主要通过 new 关键字实现,JVM 将使用堆内存区为对象分配内存。对象的销毁主要通过垃圾回收机制实现。
堆内存区的垃圾回收机制
在 Java 程序中,垃圾回收机制是自动进行的。当 JVM 发现一个对象没有被引用时,就会将该对象标记为垃圾对象,并在空闲时间内对其进行回收。
4. 虚拟机栈
虚拟机栈主要用于存放 Java 方法的执行环境。每当一个方法被调用时,就会在虚拟机栈中创建一个栈帧(stack frame)用于存储该方法的局部变量表、操作数栈、动态连接、方法返回地址等信息。
栈帧
栈帧是虚拟机栈的基本单位,用于存储一个方法的执行环境。
方法调用的过程
通过 JVM 的方法调用指令,可以在虚拟机栈中创建新的栈帧,并将调用的方法入栈。当方法执行完毕时,会将其出栈并清除其对应的栈帧。
5. 本地方法栈
本地方法栈与虚拟机栈的作用类似,不同之处在于本地方法栈主要用于处理 Native 方法(即使用非-Java 语言实现的方法)。
本地方法调用的过程
本地方法调用过程与 Java 方法调用类似,不同之处在于本地方法栈采用了所谓的本地连接,将 JVM 和本地方法库连接起来,从而实现 Native 方法的调用。
6. 程序计数器
程序计数器主要用于记录程序运行的位置。在 Java 程序中,它主要用于记录当前线程所执行的字节码的位置,以便实现线程部分切换。
程序计数器在多线程环境中的作用
在多线程运行时,每个线程都有一个独立的程序计数器,用于控制线程的执行流程和调度。
三、方法区
1. 方法区概述
方法区是 JVM 运行时数据区的一部分,主要用于存储已经被 JVM 加载的类、接口、方法的字节码信息。
2. 方法区的特点
方法区是线程共享的,因此通常它比堆空间拥有更小的空间。另外,方法区中存储的内容一般较为稳定,因此对应的垃圾回收机制采用的是“永久代”的形式。
3. 常量池
常量池是方法区的一部分,用于存储类文件中常量信息的集合。在方法区中,常量池通常被实现为一个哈希表(Hash Table)。
public class Demo {
// 字符串常量
public static final String HELLO = "hello";
// 数字常量
public static final int NUMBER = 100;
// 枚举常量
public enum Color {
RED, GREEN, BLUE
}
// 注解常量
@Retention(RetentionPolicy.RUNTIME)
public @interface MyAnnotation {
String value();
}
}
4. 类信息
类信息主要包括类的属性、方法和构造方法等信息。
public class Person {
// 属性
private String name;
private int age;
// 构造方法
public Person(String name, int age) {
this.name = name;
this.age = age;
}
// 方法
public void sayHello() {
System.out.println("Hello, My name is " + name + ", age " + age);
}
}
5. 字段信息
字段信息主要用于描述类、接口、枚举和注解类型的属性。
public class Student {
// 姓名字段
private String name;
// 年龄字段
private int age;
// 学号字段
private String id;
// 课程列表字段
private List<String> courses;
}
6. 方法信息
方法信息用于描述类或接口的方法,包括方法名称、参数数量、返回值类型等。
public class Calculator {
// 定义加法方法
public int add(int a, int b) {
return a + b;
}
// 定义减法方法
public int sub(int a, int b) {
return a - b;
}
// 定义乘法方法
public int mul(int a, int b) {
return a * b;
}
// 定义除法方法
public double div(int a, int b) {
if (b == 0) {
throw new IllegalArgumentException("除数不能为 0");
}
return (double) a / b;
}
}
四、堆内存区
1. 堆内存区概述
堆内存区主要用于存放 Java 对象。在 JVM 运行时,会在堆内存区创建对象实例,并在空闲时进行垃圾回收。
2. 堆内存区的特点
堆内存区是 JVM 运行时数据区的一部分,是线程共享的。在 Java 程序中,通过 new 关键字为对象分配内存。
3. 对象的创建和销毁
在 Java 程序中,对象的创建主要通过 new 关键字实现,JVM 将使用堆内存区为对象分配内存。对象的销毁主要通过垃圾回收机制实现。
public class Person {
private String name;
public Person(String name) {
this.name = name;
}
public void sayHello() {
System.out.println("Hello, My name is " + name);
}
}
public class Demo {
public static void main(String[] args) {
Person p = new Person("Tom");
p.sayHello();
}
}
4. 堆内存区的垃圾回收机制
在 Java 程序中,垃圾回收机制是自动进行的。当 JVM 发现一个对象没有被引用时,就会将该对象标记为垃圾对象,并在空闲时间内对其进行回收。
public class Person {
private String name;
public Person(String name) {
this.name = name;
}
public void sayHello() {
System.out.println("Hello, My name is " + name);
}
@Override
protected void finalize() throws Throwable {
System.out.println(name + " 被回收了");
super.finalize();
}
}
public class Demo {
public static void main(String[] args) {
Person p = new Person("Tom");
p = null;
System.gc();
}
}
五、虚拟机栈
1. 虚拟机栈概述
虚拟机栈主要用于存放 Java 方法的执行环境。每当一个方法被调用时,就会在虚拟机栈中创建一个栈帧(stack frame)用于存储该方法的局部变量表、操作数栈、动态连接、方法返回地址等信息。
2. 虚拟机栈的特点
虚拟机栈是线程私有的,每个线程都有一个独立的虚拟机栈。在 Java 程序中,通过方法调用指令来操作虚拟机栈。
3. 栈帧
栈帧是虚拟机栈的基本单位,用于存储一个方法的执行环境。每个栈帧包含了方法的局部变量表、操作数栈、动态连接、方法返回地址等信息。
public class Calculator {
// 定义加法方法
public int add(int a, int b) {
int c = a + b;
return c;
}
}
4. 方法调用的过程
通过 JVM 的方法调用指令,可以在虚拟机栈中创建新的栈帧,并将调用的方法入栈。当方法执行完毕时,会将其出栈并清除其对应的栈帧。
public class Demo {
public static void main(String[] args) {
int c = add(1, 2);
System.out.println(c);
}
public static int add(int a, int b) {
return a + b;
}
}
六、本地方法栈
1. 本地方法栈概述
本地方法栈与虚拟机栈的作用类似,不同之处在于本地方法栈主要用于处理 Native 方法(即使用非-Java 语言实现的方法)。
2. 本地方法栈的特点
本地方法栈与虚拟机栈的作用类似,也是线程私有的。在 Java 程序中,本地方法调用过程通过本地连接实现。
3. 本地方法调用的过程
本地方法调用过程与 Java 方法调用类似,不同之处在于本地方法栈采用了所谓的本地连接,将 JVM 和本地方法库连接起来,从而实现 Native 方法的调用。
public class Demo {
public static native int add(int a, int b);
static {
System.loadLibrary("native");
}
}
七、程序计数器
1. 程序计数器概述
程序计数器主要用于记录程序运行的位置。在 Java 程序中,它主要用于记录当前线程所执行的字节码的位置,以便实现线程部分切换。
2. 程序计数器的特点
程序计数器是线程私有的,每个线程都会拥有自己独立的程序计数器。在 Java 虚拟机内部,程序计数器是一个整数,它的大小与本地指针的宽度相同。
3. 程序计数器在多线程环境中的作用
在多线程运行时,每个线程都有一个独立的程序计数器,用于控制线程的执行流程和调度。
public class Demo {
public static void main(String[] args) {
int a = 1;
int b = 2;
int c = a + b;
int d = a - b;
System.out.println(c);
文章评论