虽然JAVA程序不用像C和C++程序员一样自己处理内存问题。但是作为一名合格的JAVA程序员还是很有必要去了解一下JVM的内存划分和使用情况的。特别是当你遇到内存溢出的情况时候,你就会发现不掌握JVM的内存知识是玩儿不转滴!
下面我就简单的说下JVM的划分情况。如下图:
1.程序计数器:线程隔离的数据区
2.虚拟机栈:线程隔离的数据区
3.本地方法栈:线程隔离的数据区
4.方法区:所有线程共享的数据区
5.堆:所有线程共享的数据区
程序计数器: 记录当前线程所执行的字节码的行号指示器,在虚拟机的概念模型里,字节码解释器工作时来选取下一个需要执行的字节码指令,分支,循环,跳转,异常处理,线程恢复等基础功能等都需要这个计数器来完成。
每条线程都有一个独立的程序计数器,各条线程间的程序计数器互相不受影响,独立存储。这类内存是线程私有的。这个区域不会出现内存溢出情况。
虚拟机栈: 虚拟机栈也是线程私有的,它的生命周期与线程相同。每个方法在执行的同时都会创建一个栈帧(Stack Frame)用于存储局部变量表,操作数栈,动态链接,方法出口等信息。每个方法从调用直至执行完成的过程,就对着栈帧在虚拟机中如栈出栈的过程。大家常说的堆栈中的栈指的就是这个虚拟机栈。虚拟机栈中存有局部变量表。局部变量表存放了编译期可知的各种基本数据类型(boolean,byte,char,short,int,float,long,double),对象引用(reference类型它不等同于对象本身,可能是指向对象起始地址的引用指针)和returnAddress(指向了一条字节码指令的地址)类型。其中Long和double会占用两个局部变量空间,其余的只占用一个空间。局部变量表在编译期完成分配。在该区域存在两种异常情况,线程请求的栈深度大于虚拟机所允许的栈深度将抛出StackOverFlowError。如果扩展无法申请到足够的内存,就会抛出OutOfMemoryError异常。
本地方法栈:本地方法栈与虚拟机栈所发挥的作用是相似的。区别就是虚拟机栈为虚拟机执行JAVA方法,本地方法栈执行虚拟机使用到的Native方法服务。有的虚拟机(SunHotSpot)把两个方法栈合二为一。本地方法栈也会抛出StackOverFlowError和OutoOfMemeoryError异常。
JAVA堆:是JAVA虚拟机中管理最大的一块内存,JAVA堆被所有线程共享。在虚拟机启动的时候创建,唯一目的就是存放对象实例。所有对象实例和数组都在这里分配。JAVA堆是垃圾收集器管理的主要区域。
方法区:方法区也是线程共享区域,它用于存储已被虚拟机加载的类信息,常量,静态变量,即时编译器编译后的代码等数据。虽然JAVA虚拟机规范把方法区描述为堆的一个逻辑部分,但是他却有个别名Non-Heap.目的是应该与Java堆区分开。对于HotSpot虚拟机来说,方法区就是永久代。
运行时常量池:运行时常量池是方法区的一部分。Class文件除了有类的版本,字段,方法,接口等信息描述外,还有一项是常量池,用于存放编译期生成的各种字面量和符号引用,这部分内容在类加载后进入方法区的运行时常量池存放。
直接内存:这部分是NIO操作的内存部分。NIO引入了Channel和Buffer。他可以利用Native函数库直接分配堆外内存,然后通过一个存储在堆中的DirectByteBuffer对象作为这块内存的引用进行操作。