注:本文主要参考《深入理解Java虚拟机 第2版》
1、概述
Java具有一次编写,到处运行(Write once, run anywhere) 的特性,而这一特性的实现JVM(Java虚拟机)在其中扮演着非常重要的角色,Java代码由编译器编译为字节码,再由JVM进行执行。JVM摆脱了硬件平台的束缚,提供了一个相对安全的内存管理和访问机制供Java执行,因此了解JVM的内存模型对于一名Java程序员是非常重要的。
2、JVM内存模型
JVM虚拟机在运行时,内存划分如下图所示:
图中的每部分内存区域的用途如下:
- 程序计数器:线程私有的内存空间,它可以看做是当前线程所执行的字节码的行号指示器。字节码解释器工作时就是通过改变这个计数器的值来选取下一个需要执行的字节码指令,分支、循环、跳转、异常处理,线程恢复等基础功能都需要依赖这估计数器来完成;
- Java虚拟机栈:线程私有内存空间,它的生命周期与线程相同,虚拟机描述的是Java方法执行的内存模型,每个方法在执行的同时会创建一个栈帧,用于存储局部变量表、操作数栈、动态链接、方法出口等信息,每个方法从调用直至执行完成的过程,就对应着一个栈帧在虚拟机中入栈到出栈的过程,调用栈深度大于虚拟机所允许的深度,将抛出StackOverflowError异常,如果虚拟机栈可以动态扩展,如果扩展时无法申请到足够的内存会抛出OutOfMemoryError异常;
- 本地方法栈:线程私有内存,与虚拟机栈所发挥的作用是非常相似的,它们之间的区别不过是虚拟机栈为虚拟机执行Java方法(也就是字节码)服务,而本地方法栈则为虚拟机使用到的Native方法服务,同样会抛出StackOverflowError和OutOfMemoryError异常;
- Java堆:堆是Java虚拟机所管理的内存中最大的一块,Java堆是被所有线程共享的一块内存区域,在虚拟机启动时,此内存区域的唯一目的就是存放对象实例,几乎所有的对象实例都在这里分片内存,但是随着JIT编译器的发展与逃逸分析技术逐渐成熟,栈上分配、标量替换优惠技术将会导致所有对象都分配在堆上也渐渐变得不那么绝对了,此块内存是垃圾回收器主要管理的区域,基于垃圾回收器算法,会细分为新生代、老年代,或者Eden空间、From Survivor空间、To Survivor空间等,具体细节需要了解垃圾回收器算法;
- 方法区:与堆一样,是各个线程共享的区域,用于存储已经被虚拟机加载的类信息、常量、静态变量、即时编译器编译后的代码等数据,也有称为“永久代”,此区域其中包含了运行时常量池,用于存储编译器产生的各种字面常量和符号引用;
- 直接内存:直接内存并不是虚拟机运行是数据区的一部分,月不会死Java虚拟机规范中定义的内存区域,但是这部分内存也被频繁的使用,而且也可能导致OutOfMemoryError一次出现,它的分配不会受到Java堆大小的限制,但是会受到本机总内存大小以及处理器寻址空间的限制,所以也要注意直接内存出现溢出的情况;
3、小结
了解Java虚拟机的内存模型,对于我们了解垃圾收集器的算法实现原理,以及内存分配策略非常有帮助。