JVM系统结构图 从图中可以看出在运行时数据区可分为三个部分,分别为方法区、堆以及栈。下面将对这三部分区域进行分析。
方法区 方法区主要存放的东西: 也就是在运行时,静态常量,类信息(也就是class文件以及引进的一些第三方的jar包)这些类模板都存放在方法区,但是注意:实例变量存放在堆内存中。
栈区 对于栈这种数据结构的定义一般都是先进后出,后进先出。那联想到我们方法的调用过程,如下代码所示:
public class StackTest { public static void test01(){ System.out.println("test01 start"); test02(); System.out.println("test01 end"); } public static void test02(){ System.out.println("test02 start"); System.out.println("test02 end"); } public static void main(String[] args) { System.out.println("main start"); test01(); System.out.println("main end"); } }我们来看运行结果: 如下示意图: 方法的调用与结束就是一个栈的数据结构,由main方法入口,main方法最先压栈,再是test01方法压栈,再是test02方法压栈,test02方法执行完后弹栈,此时PC寄存器将指针移动到test01方法,test01再弹栈,最后是main方法弹栈。
如果我们不断将一个方法递归调用,此时就会出现 也即是栈内存溢出。 栈中主要存放局部变量,以及对象的引用地址
堆内存 堆内存中保存了所有引用类型的真实信息,一个JVM实例只存在一个堆内存,堆内存的大小是可以调节的。
堆内存的结构示意图: 堆空间主要存在三块区域,新生区(对象的创建和销毁)、老年区,以及永久区,其实永久区严格意义上来讲是属于方法区的。那么这个新生区和老年区该怎么理解?
新生区的结构示意图: 新生区又分为伊甸区、幸存0区和幸存1区、其内存比例大致为8:1:1,伊甸区最大,因为对象的创建都在伊甸完成,当伊甸区内存将要满时会触发GC,GC会将有引用指向的对象复制到幸存0区,当幸存0区内存将要满时再次发生GC,将有引用指向的对象复制到幸存1区,如此反复循环,当某个对象经历了15次GC还没有被回收,则会被移到养老区。
若养老区的内存也即将满,则会进行Full GC,注意:Full GC是发生在养老区的。如果Full GC后依然没有空间保存新进入的对象,则就会报OutOfMemoryError异常!
如: