一、内存区域介绍
Java虚拟机(JVM)内存可以分为以下几个区域:
二、代码分析
代码段一:
public class MemoryExample {
// 静态变量-存储在方法区
private static String staticVariable = "Hello, World!";
// 实例变量-存储在堆中
private String instanceVariable;
public void method() {
// 局部变量-存储在栈中
int localVar = 10;
System.out.println(localVar);
// 对象实例化-对象存储在堆中,变量存储在栈中
MemoryExample obj = new MemoryExample();
obj.instanceVariable = "Java Memory";
}
public static void main(String[] args) {
method(); // 静态方法调用-存储在栈中
// 程序计数器-记录当前线程执行的字节码指令的地址
int pc = 0;
// 垃圾回收器对堆进行内存回收和分配
Object obj = new Object();
obj = null;
System.gc();
// 本地方法栈-存储本地方法的信息
nativeMethod();
}
// 本地方法-不在Java虚拟机中执行,而是在本地操作系统中执行
private static native void nativeMethod();
}
上述代码中,静态变量 staticVariable
存储在方法区,实例变量 instanceVariable
存储在堆中,局部变量 localVar
存储在栈中。静态方法 main()
存储在栈中,程序计数器记录当前线程执行的指令地址。 代码中还展示了对象的实例化,对象存储在堆中,变量存储在栈中。示例中的 MemoryExample obj = new MemoryExample();
在堆中创建了一个对象实例,并将其引用存储在栈中的变量 obj
中。 此外,示例中还包含了对垃圾回收器的调用 System.gc()
,用于对堆进行内存回收和分配。还展示了本地方法 nativeMethod()
,它不在Java虚拟机中执行,而是在本地操作系统中执行,相关信息存储在本地方法栈中。
对于这个代码示例中的私有静态变量 staticVariable
,实际上,它的初始化值 "Hello, World!" 也会存储在运行时常量池中。 在Java中,字符串常量字面量会被存储在运行时常量池中。当一个类被加载到内存中时,它的静态变量也会被初始化。对于字符串类型的静态变量,如果其初始化值是一个字符串常量字面量,那么该字符串常量字面量也会被存储在运行时常量池中。 因此,在代码示例中,初始化值 "Hello, World!" 会被存储在运行时常量池中。而静态变量 staticVariable
则是存储在方法区中,它持有对运行时常量池中 "Hello, World!" 字符串常量的引用。 需要注意的是,虽然 staticVariable
的值是一个字符串常量,它并不是直接存储在运行时常量池中,而是存储在方法区中的静态变量区域,并且该变量在运行时常量池中引用了对应的字符串常量。
代码段二:
public class MemoryExample {
public static void main(String[] args) {
// 字符串常量存储在运行时常量池中
String str1 = "Hello";
String str2 = "World";
String str3 = str1 + str2; // 字符串拼接会在运行时常量池中创建新的字符串对象
System.out.println(str3);
// 类的全限定名存储在运行时常量池中
String className = MemoryExample.class.getName();
System.out.println(className);
// 常量引用存储在运行时常量池中
final int constantValue = 10;
System.out.println(constantValue);
}
}
在上述代码中,字符串常量 "Hello"
和 "World"
存储在运行时常量池中。通过字符串拼接操作 str1 + str2
,会在运行时常量池中创建新的字符串对象 "HelloWorld"
。类的全限定名 MemoryExample.class.getName()
也会存储在运行时常量池中。最后,常量引用 constantValue
的值 10
也会存储在运行时常量池中。 尽管代码示例中没有直接访问运行时常量池,但编译器和虚拟机会自动处理和使用运行时常量池中的数据,以满足程序的运行需求。
在代码示例中,字符串常量 "Hello"
存储在运行时常量池中,而变量 str1
存储在栈中,它持有对运行时常量池中字符串常量 "Hello"
的引用。 当代码执行到 String str1 = "Hello";
这一行时,会先在运行时常量池中查找是否存在字符串常量 "Hello"
。如果存在,那么变量 str1
就会直接引用该字符串常量;如果不存在,那么会在常量池中创建一个新的字符串常量 "Hello"
,然后变量 str1
引用这个新创建的字符串常量。 所以,变量 str1
存储在栈中,它的值是对运行时常量池中字符串常量 "Hello"
的引用。而字符串常量 "Hello"
存储在运行时常量池中,它的值是字符串的实际内容。