Java内存区域
运行时数据区域
Java 虚拟机在执行 Java 程序的过程中会把它管理的内存划分成若干个不同的数据区域。
线程私有的:
- 程序计数器
- 虚拟机栈
- 本地方法栈
线程共享的:
- 堆
- 方法区
- 直接内存 (非运行时数据区的一部分)
程序计数器
程序计数器是一块较小的内存空间。可以看作为当前线程所执行的字节的行号指示器。
字节码解释器工作时就是通过改变这个计数器的值来选取下一个需要执行的字节码执行。它是程序控制流的指示器,分支,循环,跳转,异常处理,线程恢复等基础功能都需要以依赖这个计数器完成。
Java虚拟机的多线程通过线程轮流切换的方式来分配处理器执行时间。为了线程切换后能恢复到正确的执行位置,每条线程都需要独立的程序计数器,各条线程之间计数器互不影响,独立存储。我们称这类内存区域为“线程私有”的内存。
如果线程正在执行的是一个Java方法,这个计数器记录的是正在执行的虚拟机字节码指令的地址。如果还是Native方法,则为空 Undefined。
这个内存区域是唯一一个在虚拟机中没有规定任何OutofMemoryError情况的区域。
Java虚拟机栈 VM Stack
Java虚拟机栈也是线程私有的。虚拟机栈描述的是Java方法执行的线程 内存模型:每个方法被执行的时候,Java虚拟机都会同步创建一个栈桢 Stack Frame 用来存储局部变量表,操作数栈,动态连接,方法出口等信息。每个方法被调用直至执行完毕的过程,就对应着一个栈桢在虚拟机栈从_入栈到出栈_的过程。
本地方法栈 Native Method Stack
本地方法栈和Java虚拟机栈作用类似,区别是虚拟机栈为执行Java方法服务,而本地方法栈则为虚拟机执行本地方法(Native)服务。
Java堆
Java堆是虚拟机所管理的内存中最大的一块。可以被所有线程共享的一块内存区域。
在虚拟机启动时创建,此内存区域的唯一目的就是存放对象实例。即所有的对象实例以及数组都应当在堆上分配内存。
Java堆是垃圾收集器管理的内存区域。由于现代垃圾收集器大部分基于分代收集理论设计的。以G1收集器的出现为分界,现在主流的HotSpot虚拟机。其内部的垃圾收集器全部基于经典分代来设计,需要新生代,老年代收集器搭配才能工作。
但现今,垃圾收集器技术不可同日而语。HotSpot也出现不采取分代设计的新垃圾收集器。
Java堆即可以固定大小,也可以是可扩展的。通过参数-Xmx和-Xms设定。如果堆中内存没有完成实例分配,并且堆也无法在扩展时。会抛出异常OutOfMemoryError异常。
方法区
Method Area方法区也是各个线程共享的内存区域。它用于存储已被虚拟机加载的类型信息,常量,静态变量,即时编译器编译后的代码缓存等数据。
《Java虚拟器规范》对方法区的约束是非常宽松的。除了和Java堆一样不需要连续的内存和可以选择固定的大小或可扩展外,甚至可以选择不实现垃圾收集。这个区域的内存回收的目标主要是针对常量池的回收和对类型的卸载。
如果方法区无法满足新的内存分配需求时,将抛出OutOfMemoryError异常。
注意:
JDK7及其之后的版本的Hotspot虚拟机选择把静态变量随Class对象存储到堆中。
运行时常量池
运行时常量池是方法区的一部分。Class文件不仅包含类的版本,字段,方法,接口等描述信息外,还有一项就是常量池表 Constant Pool Table。用于存放编译期 生成的各种字面量与符号引用,这些将在类加载后存放到方法区的运行时常量池中。
JDK1.8之前
JDK1.8之后