Java虚拟机(JVM)中的内存区域可以分为以下几个部分:
方法区(Method Area):
堆(Heap):
Java栈(Java Stack):
本地方法栈(Native Method Stack):
程序计数器(Program Counter Register):
注意:
ByteBuffer.allocateDirect()方法分配的,并且是在Java堆之外的。对于Java内存管理,重要的是理解对象何时和如何被创建、如何存活,以及如何被垃圾收集。不同的垃圾收集策略和算法可能会影响对象如何从年轻代移动到老年代,以及什么时候被清理。
public class MemoryDemo {
// 静态变量,存储在方法区
static String staticVar = "Static Variable";
// 实例变量,存储在堆上的对象内
String instanceVar = "Instance Variable";
public static void main(String[] args) {
// 局部变量,存储在Java栈上
int localVar = 10;
// 创建对象,存储在堆上
MemoryDemo demo = new MemoryDemo();
// 调用方法,产生新的栈帧在Java栈上
demo.method1(localVar);
}
public void method1(int param) {
// param 是方法的局部变量,存储在Java栈上
// 下面是另一个局部变量,也存储在Java栈上
double localMethodVar = 2.5;
// 动态创建字符串,这将在堆上创建新对象
String dynamicStr = new String("Dynamic String");
// 调用另一个方法,这将在Java栈上创建另一个栈帧
method2(dynamicStr);
}
public void method2(String str) {
// str 是从method1传递过来的,存储在Java栈上
System.out.println(str);
}
}
我们将会按部分深入解读这段代码:
方法区:
staticVar 是一个静态变量,它存储在方法区。所有的MemoryDemo实例都共享这个静态变量。堆:
instanceVar 是一个实例变量,当一个新的MemoryDemo对象实例化时,它将被存储在堆上的那个对象内。public static void main(String[] args) {
// 局部变量,存储在Java栈上
int localVar = 10;
// 创建对象,存储在堆上
MemoryDemo demo = new MemoryDemo();
// 调用方法,产生新的栈帧在Java栈上
demo.method1(localVar);
}
Java栈:
main方法开始执行,一个新的栈帧被推入Java栈。localVar 和 demo 是main方法的局部变量,它们被存储在这个栈帧中。堆:
new MemoryDemo() 创建一个新的MemoryDemo对象,该对象将被存储在堆上。instanceVar 是这个新创建的对象的一部分,并在堆上的这个对象中存储。public void method1(int param) {
// param 是方法的局部变量,存储在Java栈上
// 下面是另一个局部变量,也存储在Java栈上
double localMethodVar = 2.5;
// 动态创建字符串,这将在堆上创建新对象
String dynamicStr = new String("Dynamic String");
// 调用另一个方法,这将在Java栈上创建另一个栈帧
method2(dynamicStr);
}
Java栈:
method1被调用时,另一个新的栈帧被推入Java栈顶部。param 和 localMethodVar 是此方法的局部变量,并存储在新的栈帧中。堆:
new String("Dynamic String") 动态创建一个新的字符串对象,这个对象存储在堆上。public void method2(String str) {
// str 是从method1传递过来的,存储在Java栈上
System.out.println(str);
}
method2被调用时,再次推入一个新的栈帧至Java栈。str 是这个方法的局部变量,它存储在这个新的栈帧中。此变量引用了在堆上创建的dynamicStr对象。这就是Java如何在其内存模型中管理变量和对象的方式。每个方法调用产生一个新的栈帧,每个新对象都存储在堆上,并且每个类的静态变量都存储在方法区。
用另简单的例子来详细说明Java内存中的这些区域如何工作。
假设我们有以下Java代码:
public class Student {
private static String schoolName = "ABC High School";
private String name;
private int age;
public Student(String name, int age) {
this.name = name;
this.age = age;
}
public static void main(String[] args) {
String localVariable = "This is a local variable";
Student studentA = new Student("John", 15);
calculateAge(2023, studentA.age);
}
public static int calculateAge(int currentYear, int birthYear) {
int age = currentYear - birthYear;
return age;
}
}
方法区:
Student类的元数据,如类名、父类名(Object),方法(main和calculateAge)和变量信息。schoolName这个静态变量也会存储在方法区。堆:
main方法中通过new Student("John", 15)创建一个新的Student对象时,该对象实例(及其实例变量name和age)被存储在堆中的Eden区。Java栈:
main方法被调用时,一个新的栈帧被推到Java栈顶,里面包含了args、localVariable和studentA这些局部变量。calculateAge方法被调用时,又会有一个新的栈帧被推到Java栈的顶部,这个栈帧包含了currentYear、birthYear和age这些局部变量。calculateAge方法返回后,其对应的栈帧从Java栈中弹出。当main方法返回后,其对应的栈帧也从Java栈中弹出。程序计数器:
为了更直观地理解,想象Java内存为一个仓库,其中不同的区域有各自的作用。当你执行一个Java程序时,这些区域都在持续地进行分配和回收工作,以确保程序的高效运行。