浅谈JVM内存模型
众所周知,Java虚拟机管理的内存分为五大区域,分别是:虚拟机栈、本地方法栈、程序计数器、方法区和堆。如下图所示:
下面我们分别来看下这五大内存区域的作用。
程序计数器:我们都知道,CPU在某一时刻只会执行一个线程中的一条指令,当时间片用完后当前线程就要让出CPU给其他线程执行,当CPU切换回当前线程时就需要恢复到之前执行到的正确的位置,PC寄存器用来存储指向下一条指令的地址,也就是将要执行的指令代码,每个线程都需要有独立的一个程序计数器,不同线程之间的程序计数器互不影响,独立存储,因此程序计数器是线程私有的。所以也可以说程序计数器是用来保存线程当前的执行现场的,以方便CPU不停的切换各个线程。需要注意的是,如果线程执行的是native方法,则计数器为空。由于程序计数器只需要保存当前线程需要执行的下一条指令的地址,因此所需要的内存空间很小,因此也就不会出现内容溢出,所以Java虚拟机规范中并没有为这块内存区域设置OutOfMemoryError错误。
Java虚拟机栈:简单的说Java虚拟机栈就是用来保存当前正在执行的方法的信息的,包括局部变量、操作数、动态链接、方法返回地址。当开始调用一个方法时就会创建一个栈帧,并压入虚拟机栈,当方法执行完毕时,再将当前栈帧出栈。虚拟机栈结构如下图所示:
本地方法栈:如果你打开jdk安装目录,可以看到有很多用C编写的文件,这些就是Java native方法所调用的C代码,可见JVM 也会使用到很多native方法,因此JVM也需要提供对应的内存区域来存储调用本地方法的信息,本地方法栈就是用来为虚拟机使用的native方法提供服务的,它的功能和Java虚拟机栈类似,只不过虚拟机栈执行的是Java方法服务。
堆:就是存放大量对象实例的地方,我们new出来的对象都存储在这里,堆存放的对象是线程共享的,因此多线程操作时需要考虑同步机制。需要明确的是前面谈到的Java虚拟机栈只保存基本数据类型的对象以及自定义对象的引用,而对象本身都是存储在堆中的,Java的垃圾回收器会主动回收堆中不再使用的对象,因此堆是GC所管理的主要区域。
方法区:存储已经被虚拟机加载的类信息、常量、静态变量等。比如对于使用static修饰的变量在加载时候就会被加载到方法区中。可以认为方法区存储的是编译器编译后的代码,方法区也叫静态区,和堆一样,被所有线程所共享。
总结:
程序计数器用来保存当前线程下一条将要执行的指令的地址,用于CPU切换线程时能够恢复到之前执行到的正确的位置;
Java虚拟机栈用来保存当前正在执行的Java方法的栈帧信息;
本地方法栈和Java虚拟机栈类似,只不过它是用来保存虚拟机正在执行的native方法的栈帧信息;
堆是用来存放对象本身的(栈是用来运行程序的),存放的仅仅是对象的引用;
方法区用来保存编译后的代码,包含类信息、常量、静态变量等。