内存管理是开发者必须掌握的基本功,不然程序总是会在各种难以捉摸的错误中崩溃,一些语言,例如 C、C++ 开发者们自己申请内存,使用完自己释放,但是不当的代码书写习惯往往导致内存泄露,引用空指针等等错误,而 Java 借助于虚拟机帮我们完成了许多工作,使开发者从内存管理的深坑中爬出来了,但是由于隔着这层虚拟机,出现问题时的应对策略更显功力,需要对虚拟机内存管理机制的深入了解。
这篇文章只是粗浅的介绍下虚拟机内存区域的大概分布,让初学者在脑海中有个大概印象,而印象的开始则借助于下面的一幅图:
Java 内存区域分为大的两个区域,一部分是线程共享的,另一部分则是每个线程所独有的。刚开始了解编程时,大致就有印象,栈内存存在于方法体中,定义的那些变量什么的都是栈内存,方法结束就没了,堆内存则是使用 new 关键字申请出来的。当然这只是粗线的认识,下面一块块的说上图中的内存分布。
类似于 CPU 中的 PC 寄存器,用于存放下一条指令的地址,但是虚拟机不使用 CPU 的程序计数器,而是自己在内存里设立一片区域模拟 CPU 的程序计数器。改变计数器的值来选取下一条需要执行的字节码指令,包括分支、循环、跳转、异常、线程恢复等基础功能都依赖于计数器。
Java 的每个线程都有其独立的计数器,计数器之间互不影响,这样当多线程操作时,一个挂起的线程在恢复时,仍然能够从计数器中恢复之前运行到的地方,继续执行。所以说程序计数器也是线程隔离的。执行 Java 方法时,计数器中存放的是虚拟机字节码的地址,而运行 Native 方法时则是空(undefined)。该区域不会产生 OutOfMemoryError。
Java 虚拟机栈也是线程私有的,它的生命周期等同于线程的生命周期。虚拟机栈描述的是 Java 方法执行时的内存模型。当方法执行时,会创建一个栈帧,用于存储方法执行期间所用到的数据结构,包含局部变量表,操作数栈,动态链接,方法出口等信息。
当一个方法执行时,一个包含以上元素的栈帧入栈,当方法退出时,栈帧出栈。一般我们都会知道内存区分为堆区和栈区,实际上也是个粗浅的分法,栈指的就是虚拟机栈,而其中最重要的部分就是局部变量表。
Java 的垃圾收集器是不会去回收栈上的内容的,因为栈上的内容总是随着方法的结束自动释放。局部变量表包含着各种编译期已知的基本数据类型、对象引用和 returnAddress。基本数据类型就是 Java 的 8 大基本数据类型(boolean,byte,char, short, int, float, long, double), 对象引用,你可以把它当成指向实际对象地址的指针或者一个代表对象的句柄,returnAddress 则是一条字节码指令的地址。当进入一个方法时,它所需要分配的空间在编译期就是已知的了。
在虚拟机栈中可能会报以下两种异常:
区别于虚拟机栈执行的是 Java 方法,本地方法栈则是虚拟机使用的 Native 方法服务,我们在看一些库的源码时正常定位到最后就是用 Native 方法实现的,但是在虚拟机规范里对本地方法使用的语言,使用方式进行硬性规定,所以虚拟机可以任意实现它。HotSpot 中本地方法栈和虚拟机栈是合在一起的。
至少从学习 C 语言时,我们就听说 malloc 方法会在堆上分配空间。Java 的堆上也是分配实例对象的。虚拟机规范上讲,基本所有的对象实例和数组都分配的堆上,例如上面栈上对象引用指向的对象,都是在堆上分配的。但是随着编译器技术的发展,所有对象都在堆上分配就不是那么纯粹了。
堆也是垃圾收集(GC)的主阵地。现代收集器基本都采用了分代收集算法,所以堆也可以划分为新生代和老年代,再细致点还有 Eden 空间,From Survivor 空间和 To Survivor 空间。由于虚拟机实现了自动垃圾收集,所以在 Java 中,堆中 new 出的对象是不需要手动释放的。
我们可以通过 - Xmx 和 - Xms 控制堆的默认大小,如果在堆中再也申请不到内存,则会抛出 OutOfMemoryError 异常。
方法区也是各个线程间共享的区域,一般存储已被加载的类信息、常量、静态变量、即时编译器编译后的代码等数据。一般很多人也把方法区称为永久代。其实仅仅是 HotSpot 团队把 GC 分代收集也扩展到方法区了,让垃圾收集器可以一块回收方法区的内存,但是其它的虚拟机实现没有这么做,也不存在永久代的,HotSpot 自身也已经在 JDK1.7 上移除了永久代中的常量池。
相对而言,垃圾收集在方法区是不怎么出现的。这个区域主要回收的是常量池和类型的卸载,但是实际上对二者的回收发生的条件极为苛刻,很少发生收集。
运行时常量池是方法区的一部分,Class 文件中包含常量池信息,用于存放编译期生成的各种字面量和符号引用,这部分在类加载后进入方法区的运行时常量池。这部分可以参考我的: 深入理解 JVM 类文件格式
来源: https://juejin.im/post/5a421479518825519408e0f7