程序计数器、JVM栈、本地方法栈这三个内存区域和线程是一一对应的,并且每一个线程的这三个区域相互独立互不干扰。他们都随着线程的产生而产生,线程的灭亡而灭亡。JVM栈和本地方法栈中的栈帧都随着方法的加载而入栈,随着方法的结束而出栈。
栈帧的大小是在程序员写类的时候就确定下来的。因此这三种区域的内存大小都具备确定性,当方法结束或线程结束后,这些内存空间就会自动被回收掉,所以JVM无需考虑这些区域的内存回收问题。
堆内存和方法区的内存分配和回收就不一样了,因为一个接口中的多个实现类所需要的内存可能不一样,并且一个方法中的多个分支所需要的内存也不一样,到底要执行哪个分支,这就要看运行时的情况了。所以堆内存中的数据的分配和回收都是动态的。所以垃圾回收器只关注堆内存中的数据的分配和回收。
public class A{
public Object obj0 = null;
public static void main(String[] argus){
A obj1 = new A();
A obj2 = new A();
obj1.obj0 = obj2;
obj2.obj0 = obj1;
obj1 = null;
obj2 = null;
}
}
方法区中存放的是类信息、常量、静态变量、编译后的代码,这些数据一般需要长时间使用,所以方法区中的垃圾回收的性价比比较低。在堆内存中进行垃圾回收,一次可以回收70%-95%的空间;而在方法区中进行垃圾回收的效率远远低于堆内存的回收效率。
方法区中的垃圾回收主要回收两部分数据:废弃常量、无用的类。