JVM内存区域划分
在JAVA虚拟机中,内存区域主要分为线程共享区域(存在线程安全问题)和线程私有区域(无线程安全问题)。
线程共享区域
其中线程共享区域分为堆和方法区,堆中主要用来存储跟实例对象相关的数据(例如,new出来的对象实例,JDK1.7后字符串常量也加入了堆),方法区主要存储跟类本身相关的信息(例如,类型信息,字段信息,方法表等)。
GC堆
堆通常又被称为GC堆,这里时垃圾回收最主要的区域,堆中又可以划分为新生代(Young)和老年代(Old)。其中新生代划分为三部分,分为Eden区、Survivor From区、Survivor To区(内存区域分配为8:1:1,这样能够使得有大部分的空间能够new对象),GC最频繁的区域就是Eden和Survivor From区,被称为minor GC。新产生的对象直接进入Eden区域,GC时会清理掉Eden和Survivor From区中死亡的对象(当前不再使用到的对象),仍然存活的对象会被复制到Survivor To区(每一次复制后年龄会增加1),如果年龄超过了设定的阈值,或者To区空间不够时,就会进入老年代(比如设定为15岁,超过了15岁后,就直接进入老年代)。一次清理过后,会将To区域的对象复制到From区,这样一次minor GC就完成了。老年代空间不足时,会先尝试触发Minor GC,如果之后空间还不足,则触发Major GC对老年代也进行GC。
方法区
方法区主要存储解析Class文件后获取相关信息。方法区会存储已被虚拟机加载的类信息、字段信息、方法信息、常量、静态变量、即时编译器编译后的代码缓存等数据(还包括符号引用:例如,一个类引用了另一个类,此时被引用类还未被加载,此时就用一个符号来代替表示)。
线程私有区域
线程私有区域主要是记录线程自身运行状态的栈。包括程序计数器、本地方法栈、虚拟机栈。
程序计数器:当前线程所执行的字节码的行号指示器,完成分支、循环、跳转、异常处理、线程恢复等功能。记录当前线程运行到哪里了,下一步执行什么命令,以及上下文切换后继续执行哪里。
本地方法栈:调用本地方法(有native关键字的方法)。
虚拟机栈:所有java方法的调用。
虚拟机栈
每一次进入方法的调用都会压入一个栈帧,栈帧中包含局部变量表、操作数栈、动态链接、方法返回地址。
局部变量表:存放编译时可知的数据类型(8大基本类型:int、float、short、long、char、byte、boolean、double)及对象引用(不是对象本身,类似地址来找到所需要的对象)。
操作数栈:存放中间过程的计算结果。
动态链接:调用其他方法时,链接到调用方法的地址。
方法返回地址:下一条指定执行哪里,以及返回值传递。
无论是正常return,还是throw Exception,都会将栈帧弹出。当线程请求栈的深度超过当前 Java 虚拟机栈的最大深度的时候,会抛出 StackOverFlowError 错误。如果栈的内存大小可以动态扩展,但是虚拟机在动态扩展栈时无法申请到足够的内存空间,则抛出OutOfMemoryError异常。