Java内存区域
运行时数据区
. 方法区
. 虚拟机栈
. 本地方法栈
. 堆
. 程序计数器
其中虚拟机栈、本地方法栈、程序计数器是线程隔离的,方法区和堆是线程共享的。
程序计数器
如果当前运行的是Java方法,此时计数器是正在执行的虚拟机字节码指令的地址。如果是本地方法,这个计数器值为空。
虚拟机栈
每个方法执行的时候,会创建一个栈帧,存放局部变量表、操作数栈、动态链接、方法出口等信息。局部变量表存放了编译期可知的各种Java虚拟机基本数据类型(boolean、byte、char、short、int、 float、long、double)、对象引用(reference类型,可能是指针,也可能是对象的句柄)和returnAddress 类型(指向了一条字节码指令的地址)。局部变量表所需的内存空间在编译期间完成分配。
本地方法栈
没有特别规定怎么实现,有的Java虚拟机(譬如Hot-Spot虚拟机)直接就把本地方法栈和虚拟机栈合二为一。
Java堆
Java中几乎所有的对象实例都在这里分配,“几乎”是因为由于及时编译,尤其是逃逸分析技术的日渐强大,栈上分配、标量替换优化手段导致一些微妙变化。
现代垃圾回收器大多基于分代收集理论,因此经常可以看到新生代、老年代等名词,但这并不是《Java虚拟机规范》对Java堆的细分。
Java堆可以设计可扩展的,也可以是固定大小的。当前主流都是按可扩展设计,通过参数-Xmx和-Xms设定。
方法区
用于存储已被虚拟机加载的类型信息、常量、静态变量、即时编译后的代码缓存等数据。
很多人把方法区称为永久代,这个只是因为HotSpot设计团队把收集器的分代设计扩展至方法区,或者说使用永久代来实现方法区而已,这样垃圾回收器可以管理这部分内存。到了JDK 7的HotSpot,已经把原本放在永久代的字符串常量池、静态变量等移出,而到了JDK 8,就废弃了永久代的概念,改用在本地内存中实现的元空间(Meta- space)来代替。
这个区域的内存回收主要是针对常量池的回收和对类型的卸载
运行时常量池是方法区的一部分,class文件除了有类的版本、字段、方法、接口等描述外,还有一项就是常量池表,用于存放编译期生成的各种字面量和符号引用,这些内容在类加载后存放到运行时常量池中。并非预置入Class文件中常量池的内容才能进入方法区运行时常量池,运行期间也可以将新的常量放入池中。
直接内存
在Java 1.4中加入新的NIO类,引入了一种基于通道与缓冲区的I/O方式,它可以使用Native函数库直接分配堆外内存,然后通过存储在Java堆里的DirectByteBuffer对象作为这块内存的引用进行操作,这样避免了在Java堆和Native堆中来回复制数据。
这部分内存不受Java堆大小的限制,但受限于系统总内存,所以在设置Xmx时需要注意。