JavaJVM_Partion
Java: 虚拟机分配,虚拟机维护. 出现内存溢出或内存泄露需要了解虚拟机机制才能够排查问题.
- 内存溢出:Out Of Memory,OOM。是指程序在申请内存时,没有足够的内存空间供其使用。
- 内存泄漏:Memory leak,是指程序在申请内存后,无法释放已申请的内存空间,一次内存泄露危害可以忽略,但内存泄露堆积后果很严重,无论多少内存,迟早会被占光。
.1. 程序计数器
一块较小的内存空间,可以看作是当前线程所执行的字节码的行号指示器。也就是通过改变程序计数器的值来选取下一条需要执行的字节码指令,关系到运行时程序执行的确定位置。注意的是程序计数器是线程私有的,也就是在多线程下,每个线程的执行位置相互独立。
线程执行的是Java方法
: 计数器记录的就是正在执行的虚拟机字节码指令的地址
;
线程执行Native方法
,计数器为空(
Undefined) 这块内存区域是唯一一个在Java虚拟机规范中没有规定任何OutOfMemoryError情况的区域.
.2. java虚拟栈
是线程私有的
,他的生命周期与线程相同。每一个方法从调用直至执行完成的过程,就对应着一个栈帧在虚拟机栈中入栈到出栈的过程
。Java虚拟机栈用于存储局部变量表、操作数栈、动态链接、方法出口
等信息。人们经常说的的栈内存指的是Java虚拟机栈中局部变量表部分(基本数据类型+对象应用)。存在两种异常:StackOverflowError异常(栈深度问题)
、OutOfMemoryError(扩展时内存不足)
。
局部变量表
: 存放编译器各种基本数据类型(boolean, byte, char, short,int, float, long, double)
,对象引用
(reference类型,可能是一指向对象起始地址的引用指针,或指向一个代表对象的句柄或者其他于此对象相关的位置),returnAddress类型
。- 局部变量表所需空间在
编译期间完成分配
,在方法运行期间不会改变局部变量表大小。64位long, double占用2个slot空间,其他占1个;
.3. 本地栈方法
为虚拟机使用到的
Native方法(本地方法,非Java方法)服务
。同样会抛出两种异常:StackOverflowError、OutOfMemoryError。
.4. java堆
是JVM管理内存中最大的一块,在
虚拟机启动时创建
,几乎所有的对象实例在这里分配内存
,并且是线程共享的
。Java堆也是垃圾收集器的主要管理区域
,因此Java堆又称为“GC堆”
(Garbage Collected Heap)。从内存回收的角度将Java堆细分为:新生代(Eden区、From Survivor区、To Survivor区)和老年代
,这种按代分区的原因将在JVM调优—垃圾收集器与内存分配策略介绍。当堆无法再扩展时抛出OutOfMemoryerror异常。
.5. 方法区
是
各个线程共享的内存区域
。方法区用于存储JVM加载的类信息、常量、静态变量、即时编译后的代码等数据
。在HotSpot中又称永久代(为了想管理Java堆一样管理方法区,事实证明不可行,只是当初HotSpot设计者想偷懒罢了!),这个区域的内存回收目标主要时针对常量池的回收和对类型的卸载
,但是在JDK1.7及之后的HotSpot中已经将字符串常量池从方法区中移出了。当方法区无法再扩展的时候抛出OutOfMemoryError异常。
.6. 运行时常量池
是方法区的一部分,在Class文件中除了有类的版本、字段、方法、接口等描述信息外,还有一项信息是常量池,它
用于存放编译期生成的各种字面量和符号引用
,这部分内容将在类加载后进入方法区的运行时常量池中存放
。直接引用也存储在运行时常量池中
。运行时常量池具有动态性,并非非要经过Class文件转换到常量池中,可以直接在运行期将新常量放入池中,String类的intern()方法就满足这种特性。当运行时常量池无法再申请到内存时抛出OutOfMemoryError异常。
.7. 直接内存
不是JVM运行时数据区的一部分,直接内存是在JDK1.4中新加入的NIO类
(基于通道和缓冲区的I/O方式)可以以使用Native函数库直接分配堆外内存
。在物理内存的限制下会抛出OutOfMemoryError异常。