深入理解JVM笔记1-JVM内存构成
JAVA虚拟机内存区域
1.程序计数器(Program Counter Register)
当前线程所执行的字节码的指示器,字节码解释器通过改变计数器的值选择下一条需要执行的字节码命令。
线程的程序计数器私有,分开存储,该区域没有内存溢出异常
2.虚拟机栈(Java Virtual Machine Stacks)
线程私有,生命周期与线程相同。
虚拟机栈是描述方法执行的内存模型:方法在执行的同时创建一个栈帧(Stack Frame),用于存储 局部变量表,操作数栈,动态链接,方法出口。栈帧入栈到出栈的过程对应方法的调用到执行完成。
如果将JAVA内存区域笼统的分为堆和栈的话,栈就是当前的虚拟机栈的局部变量表部分。 局部变量表存放了编译器可知的基本数据类型(byte,short,int,long,boolean,double,float,char), 对象引用(reference,不同于对象本身,可能是起始地址的引用指针,对象的句柄,或者其它与对象相关的位置), returnAddress类型(指向了一条字节码指令的地址)。 其中64的long和double会占用两个局部变量空间(Slot),其余数据类型占用一个。局部变量表所需的内存空间在编译器完成分配 方法执行期间不会改变局部变量表的大小。
在JAVA虚拟机规范中对虚拟机栈规定了两种异常状况:
1、StackOverflowError:线程请求的栈深度大于虚拟机允许的深度
2、OutOfMemoryError:虚拟机可以申请的动态扩展的内存不够
3.本地方法栈(Native Method Stack)
和虚拟机栈功能基本相同,区别为虚拟机栈执行的是JAVA方法,本地方法栈执行的是Native方法(JAVA中使用非JAVA语言实现的方法), 在HotSpot中合并了本地方法栈和虚拟机栈。
在JAVA虚拟机规范中对本地方法栈规定了两种异常状况:
1、StackOverflowError:线程请求的栈深度大于虚拟机允许的深度
2、OutOfMemoryError:虚拟机可以申请的动态扩展的内存不够
线程内存包括了程序计数器,本地方法栈和虚拟机栈三部分。
-xss 可以指定虚拟机分配给线程的内存大小,去除程序计数器之后一般等同于栈大小。
4.堆(Java堆 Heap)
所有线程共享的内存区域,虚拟机启用的时候创建。Java虚拟机规范描述为:所有的对象实例和数组都要在堆上分配。 但是随着JIT技术的发展和逃逸分析技术的成熟,栈上分配,标量替换等优化技术导致对象实例可能在栈上分配。
从内存回收的角度看:由于当前收集器都使用分代收集算法、所以JAVA堆可以分为:新生代和老年代。更细致一点可以分为 Eden空间,From Survivor空间,To Surivivor空间。
从内存分配角度看,可以分为多个线程私有的分配缓冲区(TLAB Thread Local Allocation Buffer)。
java堆可以通过 -Xms 和 -Xmx控制
异常:OutOfMemoryError:堆上没有足够的内存完成实例的分配,并且堆也没法扩展的时候
5.方法区(Methed Area)
线程共享区域,用于存储被虚拟机加载的类信息,常量,静态常量,既时编译器编译后的代码等数据。方法区的别名为非堆(Non-Heap) 对于HotSpot来说,方法区又被称为永久代(Permanent Generation),但是在其它主流的商业虚拟机中不存在永久带的概念。此区域为 Hotspot为了方便GC回收该区域而使用的使用永久代代替方法区。事实上使用永久代来代替方法区会更容易造成内存溢出的问题。
使用 -XX:MaxPermSize来设置永久代大小,而在其它商业虚拟机只要不触及操作系统的内存上限就不会存在问题,而且有些方法(String的intern()) 会应为该区域造成不同的虚拟机下有不同的表现。 JAVA虚拟机规范规定该区域可以选择不实现垃圾回收,各虚拟机可以自行决定实现方式。该区域的内存回收目标主要是针对常量池的回收和类型的卸载 一般来说这个区域的回收效果比较令人满意,尤其是类型的卸载条件非常苛刻。但是这部分的回收又是必要的,Sun的BUG列表中,曾出现的十多个严重的 BUG就是由于低版本的HotSpot虚拟机未对该区域进行回收造成的。
异常:OutOfMemoryError:方法区无法满足内存分配的需求
6.运行时常量池(Runtime Constant Pool)
方法区的一部分,Class文件除了有类的版本,字段,方法,接口等描述外,还有一项信息是常量池(Constant Pool Table) , 用于存放编译器生成的各种字面量和符号引用,这部分内容将在类加载后进入方法区的运行时常量池存放。 JAVA对于运行时常量池没有细节要求,不同的提供商实现的虚拟机可以按照自己的需要来实现这个区域。一般来说,除了保存 Class文件中描述的符号引用外,还会把翻译过来的直接引用也存储在运行时常量池。
异常:OutOfMemoryError:当方法区无法满足内存分配的需求时
7.直接内存(Direct Memory)
直接内存并不是虚拟机运行的数据区的一部分,也不是JAVA虚拟机规范定义的内存区域。
本机直接内存不受JAVA堆大小的限制,但是既然是内存,肯定会受到本机总内存(RAM和SWAP区或者分页文件)大小以及处理器寻址空间 的限制。服务器管理员设置虚拟机参数的时候,会根据实际内存设置-Xmx等参数信息,但经常忽略直接内存,使得各个内存区的总和大于服务器 内存限制(包括物理和操作系统的限制),从而导致OutOfMemoryError。
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
以下为HotSpot实际使用的内存区域:
1.堆
PS Eden Space
PS Survivor Space
PS Old Gen
2.非堆
Code Cache
PS Perm Gen
其中PS Eden Space 和 PS Survivor Space(也可拆分为两个Survivor) 为年轻代 PS Old Gen为老年代,Code Cache和PS Perm Gen为永久代(方法区)大小。