JVM主要由三部分组成:类加载器、执行引擎和运行时区域。运行时区域主要分为程序计数器、虚拟机栈和本地方法栈以及堆和栈。其中方法区和堆是线程共享,而虚拟机栈、本地方法栈和程序计数器是线程独占。如图所示:
一 静态常量池、字符串常量池和运行时常量池
1.1 静态常量池
静态常量池位于编译后的字节码文件中,用户存放字面量和符号引号。字面量既包括字符串,比如private String desc = “人”,也包括常量值,比如private static final int year = 2021。而符号引号就是用一组符号来描述引用,比如类和接口的全限定名,字段名称或者方法名称等。比如有两个类,com.ecom.A 和com.ecom.B, 其中A引用了B,在编译A的时候,并不知道B的实际地址,就通过com.ecom.B来表示。
1.2 字符串常量池
字符串常量池是方法区或者堆中用来存储字符串常量的池子,可以提升系统性能,如果字符串常量池有则返回该字符串在常量池的地址;否则需要新分配地址给这个字符串。
1.3 运行时常量池
在JVM进行类加载的时候,将字节码文件中的静态常量池内容加载到内存中,我们知道静态常量池有字面量和符号引用等,静态常量池中的字面值和符号引号就会放到运行常量池中。
但是,JDK 1.7之后运行时常量池中的字符串常量池移到了堆中;符号引用也移到了本地直接内存中。所以运行常量池在JDK1.7之后,就只包括类、字段、方法、静态变量和方法等静态常量池中的信息。JDK1.8之后,静态变量和静态方法也移到了堆内存中。
二 方法区
方法区:是JVM规范中内存区域的一部分,并不是真正的实现,在具体的实现中JDK1.6以前是永久代(Perm), JDK1.7 以后叫做元数据区(Metaspace)。方法区主要存储JVM加载的类信息(字段,方法,接口等)、静态变量、常量池。
注意:
第一:JDK1.7之后方法区中的字符串常量池移到了堆内存中,符号引号移到了直接内存。J
第二: JDK1.8之后将方法区中静态变量也移到了堆中,并且方法区剩余的东西,比如类、字段、方法元数据信息全部移到了元数据空间,而元数据空间则是分配到直接内存上的。
为什么移除永久代?
字符串存在永久代中,容易出现性能问题和内存溢出。
三 虚拟机栈
每一个方法都会在栈上创建一个栈帧的数据结构,来存储局部变量表、操作数栈等。
局部变量表: 是一个数组,用于存储方法参数和方法局部变量,是基于下标来访问的。
操作数栈:存储操作数的数组
public int foo(int a, int b) {
int c = 100;
int d = a + b;
int r = d * c;
return r;
}
四 本地方法栈
本地方法栈和虚拟机的栈差不多,只不过虚拟机栈存放的是java方法调用信息,本地方法栈存放的是本地方法调用信息
五 程序计数寄存器(Program Count Register)
每一个方法或者线程都有自己独立的程序计数器寄存器,用于记录下一条需要执行的指令的地址。因为在多线程环境下,存在上下文切换到的问题,如果之前的线程没有记录下一条需要执行哪一条指令,下一次CPU切回到这个线程的时候,就不知道下一条指令执行什么。
如果当前线程正在执行一个Java方法,则程序计数器记录正在执行的字节码地址;如果当前线程正在执行本地方法,则寄存器为空。因为这不属于JVM的范畴。
六 堆
堆也是被所有线程共享的一块内存区域,在虚拟机启动的时候创建,主要存放对象实例。在JDK1.7的时候,运行时常量池中的字符串常量池移到了堆中;JDK1.8静态变量移到了堆中。如图示:
public class Person {
private String name,gender;
private int age;
private double salary;
private static int numCreated;
private static String desc = "人";
public Person(String name, String gender, int age, double salary) {
this.name = name;
this.gender = gender;
this.age = age;
this.salary = salary;
numCreated++;
}
public void say(String word) {
System.out.println(word);
}
public void introduce() {
System.out.println(this.name + "在"+this.age+"岁的时候,薪水是"+this.salary+"元");
}
public static void show() {
System.out.println("Person已经创建了"+numCreated+"个实例");
}
}
public class Test{
public static void main(String[] args) {
Person person1 = new Person("叶诗颖","女",30,25000.00);
Person person2 = new Person("张若尘","男",25,15000.00);
Person person3 = new Person("楚灵溪","女",20,8500.00);
person1.introduce();
}
}