Java 的内存管理都是由 JVM 来进行管理的,不需要程序员手动管理内存,这对于程序员来说是友好的。 但一件事有好处就有坏处。 假如内存管理出现了问题,如果我们不了解 JVM 是如何管理内存的,那么排查问题将会是一项异常艰难的工作。
1、运行时数据区域
Java 虚拟机在执行 Java 程序的过程中会把它所管理的内存划分为若干个不同的数据区域。这些区域都有各自的用途。
1.1 程序计数器
程序计数器(Program Counter Register)是一块较小的内存空间,它可以看作是当前线程所执行的字节码的行号指示器。
作用:
(1)字节码解释器工作时就是通过改变这个计数器的值来选取下一条需要执行的字节码指令,它是程序控制流的指示器。
(2)还有一个很重要的作用就是——线程恢复,在并发如此流行的时代,线程的上下文切换是常有的事。假如发生了线程上下文切换,如何能让线程恢复之前运行的状态?就需要依赖程序计数器来完成。
特点:
(1)就根据刚刚线程恢复这个作用就能推断出来,程序计数器是线程私有的,生命周期和线程一样。
(2)当执行 Java 方法时,程序计数器中保存的值就是正在执行的虚拟机字节码指令的地址;当执行本地方法时(用 native 关键字修饰的方法),程序计数器中保存的值就是为空。
(3)程序计数器是唯 一一个在《Java虚拟机规范》中没有规定任何 OutOfMemoryError 情况的区域。
1.2 Java 虚拟机栈
虚拟机栈描述的是 Java 方法执行的线程内存模型。
作用:
每个方法被执行的时候,Java 虚拟机都会同步创建一个栈帧(Stack Frame)用于存储局部变量表、操作数栈、动态连接、方法出口等信息。
每一个方法被调用直至执行完毕的过程,就对应着一个栈帧在虚拟机栈中从入栈到出栈的过程。
局部变量表存放了编译期可知的各种 Java 虚拟机基本数据类型、对象引用、returnAddress 类型。
特点:
如果线程请求的栈深度大于虚拟机所允许的深度,将抛出 StackOverflowError 异常;如果 Java 虚拟机栈容量可以动态扩展,当栈扩展时无法申请到足够的内存会抛出 OutOfMemoryError 异常。
注意:HotSpot 虚拟机的栈容量是不可以动态扩展的,以前的 Classic 虚拟机倒是可以。
Java 虚拟机栈是线程私有的,生命周期和线程一样。