一. 栈、堆、方法区的简介
栈(stack): 1.为每个方法分配一个栈帧,栈帧之间不共享
2.存放局部变量:基本数据类型和对象的引用
堆(head): 1.存放对象
2.被所有线程共享
方法区(methodArea): 1.被所有线程共享
2.存放整个程序中永远唯一的元素:class(字节码指令),static(静态变量)和字符串常量
二. 举例子,详细阐述
以举例子的方式,介绍一个段代码在栈、堆、方法区中的内存分配情况和执行流程。
2.1 需要所举例子的元素
下面需要两个元素,第一:栈、堆、方法区的模型;第二:执行的一小段代码。
2.1.1 需要的栈、堆、方法区的模型
2.1.2 需要的代码
package test01;
public class StackHeadMethod {
public static void main(String[] args) {
int a = 1;
String b = "testBName";
Test test = new Test();
test.testMethod(a, b);
}
}
class Test {
private int testA;
private String testB;
private static final int testC = 2;
public void testMethod(int testA, String testB){
this.testA = testA;
this.testB = testB;
}
}
三、进行内存分配流程的阐述
首先,JVM将StackHeadMethod.class、Test.class装载到方法区(JVM会执行启动类装载器、扩展类装载器和类路径装载器,在此只详细讲解针对本测试代码的字节码文件的执行)。其中方法区中的2是Test.java中的静态变量,在类加载的时候就在方法区中的静态存储空间分配内存。
然后,在StackHeadMethod.class文件中,找到main方法,开始执行main方法。将main方法在栈中开辟一个空间,称为栈帧。执行下面07、08、09、17、18、19行代码。
package test01;
public class StackHeadMethod {
public static void main(String[] args) {
int a = 1; // 在栈中分配内存空间
String b = "testBName"; // 在栈和方法区中分配内存空间
Test test = new Test(); // 在栈中堆中分配内存空间
test.testMethod(a, b);
}
}
class Test {
private int testA; // 在堆中分配内存空间
private String testB; // 在堆中分配内存空间
private static final int testC = 2; // 在堆和方法区中分配内存空间
public void testMethod(int testA, String testB){
this.testA = testA;
this.testB = testB;
}
}
再然后,执行test.testMethod(a, b);这一行,在栈中新分配一个栈帧,调用test中的testMethod方法。
package test01;
public class StackHeadMethod {
public static void main(String[] args) {
int a = 1;
String b = "testBName";
Test test = new Test();
test.testMethod(a, b); // 在栈中分配空间
}
}
class Test {
private int testA;
private String testB;
private static final int testC = 2;
public void testMethod(int testA, String testB){
this.testA = testA;
this.testB = testB;
}
}
最后,testMethod方法执行完之后,testMethod栈帧从栈中释放空间,然后main方法执行完之后,main栈帧也释放空间,最后堆中的对象和方法区中的静态变量、字符串和字节码指令都没被使用时,根据java虚拟机的垃圾回收机制,进行对垃圾的回收。
以上,是执行一段代码,对内存进行开辟和释放的整个过程。