One minute
JVM内存区域
运行时数据区
Java 虚拟机在执行 Java 程序的过程中会把它管理的内存划分成若干个不同的数据区域。
JDK1.8之前的
JDK1.8 和之前的版本略有不同,
线程共享
- 堆,方法区,直接内存
线程私有
- 程序计数器,本地方法栈,虚拟机栈
堆
堆是Java虚拟机所管理的内存中最大的一块,Java堆是所有线程共享的,主要用来存放实例对象以及数组。
堆也是垃圾回收的主要区域,垃圾回收主要采用的是分代回收,分为新生代和老年代。
Java堆可以是内存上不连续的的区域,只要逻辑上联系即可。
堆空间不足时会抛出:OutOfMemoryError。
方法区
方法区和Java堆一样,也是线程共享的。方法区主要用来存放已被虚拟机加载的类信息,常量,静态变量,和编译后的代码。
方法区空间不足时会抛出:OutOfMemoryError。
运行时常量池
运行时常量池是方法区的一部分,用于存放编译期间所产生的字面量和符号引用。这部分内容将在类加载后进入方法区的运行时常量池中。
运行时常量池,相比于Class文件中的常量池一个重要的特征就是具备动态性,可以在运行期间,通过String类的intern()方法将新的常量放入池中。
运行时常量池受方法区的内存限制,当常量池无法在申请到足够的内存时会抛出: OutOfMemoryError。
程序计数器
程序计数器是虚拟机管理的内存中较小的一块,它可以看作是当前程序执行的字节码文件的行号指示器。
它是线程私有的,各线程之间的程序计数器,相互独立,互不干扰。
程序计数器是唯一一个不会出现OutOfMemoryError的内存区域。
它的生命周期和线程一样。
虚拟机栈
虚拟机栈和程序计数器一样,也是线程私有的,它的生命周期和线程相同。描述的是Java方法执行的内存模型。
每个方法在执行时,都会创建一个栈帧,栈帧中拥有局部变量表,操作数栈,动态链接,方法出口等信息。
每一次函数调用都会有一个对应的栈帧被压入java栈,函数调用结束后,都会有一个相应的栈帧被弹出。
- Java中有两种返回方式:return和抛异常,这两种方式都会导致栈帧被弹出。
Java虚拟机栈有两种异常:
- StackOverFlowError:若栈不允许动态扩展,当前线程请求的栈的深度超过当前Java虚拟机栈的最大深度时。
- OutOfMemoryError:当Java虚拟机栈无法申请到足够内存时。
本地方法栈
本地方法栈和Java虚拟机栈类似,只不过本地方法栈是为native方法服务的。
本地方法被执行的时候,在本地方法栈也会创建一个栈帧,用于存放该本地方法的局部变量表、操作数栈、动态链接、出口信息。
方法执行完毕后相应的栈帧也会出栈并释放内存空间,也会出现 StackOverFlowError 和 OutOfMemoryError 两种异常。
直接内存
直接内存并不是虚拟机运行时数据区的一部分,也不是虚拟机规范中定义的内存区域,但是这部分内存也被频繁地使用。而且也可能导致 OutOfMemoryError 异常出现。
JDK1.4 中新加入的 NIO(New Input/Output) 类,引入了一种基于通道(Channel) 与缓存区(Buffer) 的 I/O 方式,它可以直接使用 Native 函数库直接分配堆外内存,然后通过一个存储在 Java 堆中的 DirectByteBuffer 对象作为这块内存的引用进行操作。这样就能在一些场景中显著提高性能,因为避免了在 Java 堆和 Native 堆之间来回复制数据。
本机直接内存分配会受到本机总内存大小以及处理器寻址空间的限制。