Java运行时内存区域
程序计数器
程序计数器是当前线程所执行的字节码的行号指示器。
为了线程切换后能恢复到正确的执行位置,每条线程都需要有一个独立的程序计数器。所以程序计数器是线程私有
的内存。
虚拟机栈
虚拟机栈是由栈帧组成的,每个方法在执行的同时都会创建一个栈帧用于存储局部变量表、操作数栈、动态链接、方法出口等信息。
每个方法从调用直至执行完成的过程,就对应着一个栈帧在虚拟机中入栈到出栈的过程。
虚拟机栈也是线程私有
的。
该区域可能出现的异常情况:
- 如果线程请求的栈深度大于虚拟机所允许的栈深度,将抛出StackOverflowError异常。
- 如果虚拟机栈可以动态扩展,但在扩展时无法申请到足够的内存,将抛出OutOfMemoryError异常。
本地方法栈
本地方法栈与虚拟机栈发挥的作用相似,区别是虚拟机栈执行Java方法(也就是字节码)服务,而本地方法栈则为虚拟机使用到的Natvie方法服务。
Java堆
Java堆是Java虚拟机所管理的最大内存区域,几乎所有的对象实例都在这里分配内存。可利用参数 -Xms 和 -Xmx 进行堆内存控制。
这块区域也是垃圾收集器管理的主要区域,由于收集器基本都采用分代收集算法,所有堆内存也分为新生代、老年代,再细致一点的有Eden空间、Fron Survivor空间、To Survivor空间等。
Java堆是线程共享
的内存区域。
方法区(JDK 1.7)
方法区用于存储已经被虚拟机加载的类信息,常量、静态变量编译后的代码等数据。这块区域也被称为永久代。
可利用参数 -XX:PermSize -XX:MaxPermSize 控制初始化方法区和最大方法区大小。
方法区是线程共享
的内存区域。
元空间(JDK 1.8)
在 JDK1.8 中已经移除了方法区(永久代),并使用了一个元空间进行代替,使用本地内存来存放类的元数据。
默认情况下元空间会根据使用情况动态调整,避免了由于加载类过多从而出现 java.lang.OutOfMemoryError: PermGen。
可使用-XX:MaxMetaspaceSize来控制最大内存。
和永久代不同的是,元空间存储类的元信息,静态变量和常量池等并入堆中。相当于永久代的数据被分到了堆和元空间中。
运行时常量池
在JDK1.7中,运行时常量池是方法区的一部分,除了保存Class文件中的类的版本、字段、方法接口等描述信息外,还有常量池,用于存放编译器生成的各种字面量和符号引用外,还会把翻译出来的直接引用也存储在运行时常量池中。
直接内存
直接内存也称为堆外内存,并不是虚拟机运行时数据区的一部分,也不是Java虚拟机规范中农定义的内存区域。
在JDK1.4中新加入了NIO(New Input/Output)类,引入了一种基于通道(Channel)与缓冲区(Buffer)的I/O 方式,它可以使用native函数库直接分配堆外内存,然后通过一个存储在Java堆中的DirectByteBuffer对象作为这块内存的引用进行操作。这样能在一些场景中显著提高性能,因为避免了在Java堆和Native堆中来回复制数据。
本机直接内存的分配不会受到Java堆大小的限制,受到本机总内存大小限制。
配置虚拟机参数时,不要忽略直接内存,防止出现OutOfMemoryError异常。