内存区域(运行时数据区)
- JVM内存区域包括线程私有的程序计数器、虚拟机栈、本地方法栈,以及线程公有的方法区和堆。
程序计数器:
- 记录线程执行到的字节码行号,为了线程切换后能恢复到正确的执行位置,每条线程都有一个独立的程序计数器,互不影响,所以这块内存区域是线程私有的。 它还有一个作用是字节码解释器通过改变程序计数器来依次读取指令,从而实现代码的流程控制,如:顺序执行、选择、循环、异常处理。此区域是唯一 一个虚拟机规范中没有规定任何内存溢出情况的区域。
(OutOfMemoryError,即 OOM) 。
Java虚拟机栈:(存储局部变量)
- 虚拟机栈描述的是Java方法执行的内存模型,线程私有,生命周期与线程相同。每个方法在执行时都会创建一个帧栈,用于存储局部变量、操作数栈等信息,方法从调用到执行完成,对应栈帧在虚拟机栈中从入栈到出栈的过程。
- 可以通过虚拟机的 -Xss 参数来指定线程 栈内存的大小。
- 虚拟机栈有两种异常状况:如果线程请求的栈的深度大于虚拟机所允许的深度,将抛出栈溢出异常(StackOverflowError),也就是在进行递归操作时,会有个压栈的过程,如果递归次数过多导致栈的深度超过了虚拟机所允许的深度,就会出现栈溢出的异常;如果虚拟机栈扩展时无法申请到足够的内存,就会抛出内存溢出异常(OutOfMemoryError)。
本地方法栈:
- 也是线程私有的。本地方法栈与 Java 虚拟机栈类似,它们的区别是本地方法栈为本地方法服务。
- 本地方法栈也会抛出栈溢出和内存溢出的异常。
Java堆: (存储对象实例)
- Java堆是线程共享的内存区域,在虚拟机启动时创建,它的唯一目的是存放对象实例。
- 所有对象都在这里分配内存,是垃圾收集的主要区域(GC 堆)。从内存回收角度看,由于现在的收集器一般都采用分代收集算法,所以Java堆还可以细分为新生代和老年代,再细致点有 Eden 空间,From Survivor 空间和 To Survivor 空间。
- Java堆可以处在物理上不连续的内存空间中,只要逻辑上是连续的即可。可以动态扩展,空间不足时抛出 OutOfMemoryError 异常。
- 可以通过虚拟机参数来指定堆内存大小,参数 -Xms 设置初始值,参数 -Xmx 设置最大值。
方法区: (存储静态变量和常量)
- 方法区也是线程共享的内存区域,它用于存储已经被虚拟机加载的类信息、常量、静态变量等数据。
- 方法区和Java堆一样不需要连续的内存,可以动态扩展,空间不足时抛出 OutOfMemoryError 异常。这个区域内存回收的主要目标是对常量池的回收和对类型的卸载。(从 JDK 1.8 开始,把方法区移到了元空间,它位于本地内存中,而不是虚拟机内存中。)