java虚拟机所管理的内存主要包含5个运行时区:
- 方法区(Method Area)
- 虚拟机栈(VM Stack)
- 本地方法栈(Native MethodStack)
- 堆(Heap)
- 程序计数器(Program Counter Register)
程序计数器是一块比较小的内存区,它记录了当前线程所执行的字节码的行号。字节码解释器运行时根据程序计数器取下一条需要执行的字节码指令,分支、循环、跳转、异常处理、线程恢复等功能依赖这个计数器完成。
在任意一个瞬间,一个内核都只执行一个线程,每个线程都有自己的程序计数器,这是线程的“私有内存”。
如果实行的是Native方法,那么程序计数器为空,这也是虚拟机唯一没有OutOfMemeoryError的内存区。
虚拟机栈也是线程私有区,每个线程执行时都会创建一个栈帧,用来存储局部变量表、操作数栈、动态链接、方法出口等。
局部变量表存储的是编译期可以知道的各种基本数据类型、对象引用和returnAddress类型(指向一条字节码指令的地址),64位长度的long和double类型的数据占用2个局部变量空间(slot)。它的内存大小是在编译时就分配好的,运行期间不会改变。
局部变量表在2种情况下抛出异常:
- 线程请求的栈深度超过虚拟机所允许的深度,会抛出StackOverflowError
- 虚拟机栈扩展时,无法申请到足够内存,抛出OutOfMemeoryError
本地方法栈作用与虚拟机栈相似,只是它为Native方法服务。
堆是java虚拟机所管理的内存中最大的一块,是所有线程共享的区域,存放对象实例。所有的对象实例和数组在堆上分配(java虚拟机规范,也不绝对(栈上分配,标量替换))。
它是垃圾回收期管理的主要区域,也叫GC堆(Garbage Collected-Heap)。现代回收期一般都采用分代收集算法。
堆中有:新生代,老年代。细分为:Eden空间,From Survivor、To Survivor。
堆中可能划分出多个线程私有的分配缓冲区(TLAB)
-Xmx,-Xms控制堆的大小
方法区也是各个线程共享的区域,存储类信息、常量、静态变量、即时编译器编译后的代码等数据。
在HotSpot虚拟机中,方法区是用永久代实现的(有被替代的趋势,因为永久代收到堆大小的限制,而且少数方法因为这个原因在不同虚拟机下有不同表现)。
方法区的数据,在类卸载以及常量池的回收时消失。
运行时常量池存放编译器生成的各种字面量和符号引用,这些数据在类被加载时进入内存。
String的intern()方法,可以在运行期间将新的常量放入方法区。
直接内存不是虚拟机运行时内存的一部分,它使用Native函数库直接分配堆外内存,通过存储在Java堆中的DirectByteBuffer对象作为这块内存的引用进行操作。避免了在java堆和native堆中来回赋值数据,显著提高了性能。
主要在NIO(一种基于通道和缓冲区的I/O模式)中使用。