Java内存区域
1、程序计数器(Program Counter Register):
(1)、概念:一块较小的内存空间,可以看做当前线程所执行的字节码的行号指示器。
(2)、用途:字节码解释器工作的时候通过这个计数器的值选取下一条执行的字节码指令,分支、循环、跳转、异常处理、线程恢复等基础功能都需要这个计数器来完成;
(3)、线程切换后恢复到正确的执行位置,每个线程都需要一个独立的程序计数器,各个线程之间互不影响独立存储,为线程私有区域;
(4)、Java方法,该值为虚拟机字节码指令的地址,如果是Naive方法,该值为空。该内存区域是没有规定任何OutOfMemoryError情况的区域。
2、Java虚拟机栈(Java Virtual Machine Stacks)
(1)、线程私有,其生命周期与线程相同;
(2)、方法调用会创建栈帧,存储:局部变量表、操作数栈、动态链接、方法出口灯信息;
(a)、局部变量表存放了编译器可知的各种基本数据类型(boolean、byte、char、short、int、long、double),对象引用(引用地址、句柄、与对象相关的位置),
returnAdress类型(指向了一条字节码指令的地址,为字节码Jsr,jsr_w和ret服务,古老的虚拟机利用几条指令实现异常,目前几乎无用);
(b)、局部变量表的内存空间在编译器完成分配,方法运行期间不会改变局部变量表的大小;
(3)、每一个方法的调用到完成都对应于一个栈帧在操作数栈中的入栈到出栈的过程;
(4)、出现两种异常,栈深度:StackOverflowError,虚拟机栈动态扩展:OutOfMemoryError异常。
3、本地方法栈(Native Method Error)
(1)、为虚拟机使用到的Native方法服务;
(2)、出现两种异常,栈深度:StackOverflowError, 栈动态扩展:OutOfMemoryError异常。
4、Java堆(Java Heap)
(1)、Java虚拟机管理的内存中最大的一块,所有线程共享的一块内存区域,在虚拟机启动的时候创建;
(2)、该内存区域唯一的目的就是存放对象实例,几乎所有的对象实例都是在这里分配内存;
(3)、内存回收的角度,Java堆细分:新生代和老年代;
内存分配的角度:线程共享的Java堆可能划分出多个线程私有的分配缓存区(TLAB);
进步划分的目的都是更快的回收内存或者更快的分配内存。
(4)、Java堆可以处在物理上不连续的内存空间,但是逻辑上需要连续。实现的时候可以设计成固定大小也可以是可扩展的。
主流虚拟机都是可以扩展的,通过参数-Xmx和-Xms空值。
5、方法区(Method Area)
(1)、各个线程共享的内存区域,它用于存储以被虚拟机加载的类信息、常量、静态变量、即时编译后的代码等数据;
(2)、java虚拟机规范对于方法区的限制比较宽松,除了和Java对一样的不连续的内存和可以选择固定大小和可扩展外,还可以选择不实现垃圾收集。
(3)、该区域比较少存在垃圾收集的行为,但是数据进入了方法区并不是永久了,该区域的内存回收目标主要是对常量池的回收和对类型的卸载。但是条件较为苛刻。
6、运行时常量池(Runtime Constant Pool)
(1)、Class文件中的常量池,存储编译器生成的字面量和符号引用,在类加载后进入方法区的运行时常量池进行存放;
(2)、除了符合引用,还有翻译出的直接引用一般也会存储在运行时常量池。
(3)、该概念和Class文件的常量池不是同一个概念,是处于不同阶段的内容,运行时常量池相比于Class文件的常量池是具备动态性,在运行期间可以将新的常量放在池中。
(4)、该部分区域是方法区的一部分,受到方法区内存的限制。会抛出OutOfMemoryError。
7、直接内存(Direct Memory)
(1)、不是虚拟机运行时的数据区的一部分,也不是Java虚拟机规范中定义的内存区域;
(2)、会被频繁使用,可能出现OutOfMemoryError的异常;
(3)、不受Java堆大小的限制,但是会受到本机总内存的大小和处理器寻址空间的限制;
(4)、配置虚拟机参数,会忽略直接内存,使得各个内存区域的总和大于物理内存限制(物理级别和操作系统级的限制),从而出现OutOfMemoryError异常。