《Ja技术分享va虚拟机规范SE 7版》规定,Java虚拟机所管理的内存将会包括以下几个运行时数据区域。如图:

技术分享

 技术分享技术分享技术分享技术分享技术分享

 

程序计数器(Program Counter Register)是一块较小的内存空间,它的作用可以看做是当前线程所执行的字节码的信号指示器。在虚拟机的概念模式里,字节码解释器工作时就是通过改变这个计数器的值来选取下一条需要执行的字节码指令的。在多线程程序中,为了能在线程切换后,能够恢复到正确的位置开始执行,每个线程都一个独立的程序计数器。如果该方法是Java方法,这个计数器记录的是JVM正在执行的字节码指令的地址;如果正在执行的方法是Native的,那么这个计数器的值是空(Undefined)。此内存区域是唯一一个在Java虚拟机规范中没有规定任何OutOfMemoryError情况的区域。

 

Java虚拟机栈

与程序计数器一样,java虚拟机栈(Java Virtual Machine Stack)也是线程私有的。它的生命周期与线程相同。虚拟机栈描述的是Java方法执行的内存模型:每个方法被执行的时候都会同时创建一个栈帧(Stack Frame)用于存储局部变量表、操作数栈、动态链接、方法出口等信息。每一个方法被调用直至执行完成的过程就对应着一个栈帧在虚拟机栈中从入栈到出栈的过程。(C++里,在函数调用时,第一个进栈的是函数中后的下一条指令(函数调用语句的下一条可执行语句)的地址,然后是函数的各个参数,大多数的C编译器中,参数是由右往左入栈的,然后是函数中的局部变量。静态变量是不入栈的。) JVM虚拟机栈的局部变量表存放了编译器可知的各种原始类型、对象引用、returnArress类型(指向了一条字节码指令的地址)。

JVM stack 可以被实现成固定大小,也可以根据计算动态扩展。可以使用-Xss参数调整栈内存容量。

JVM Stack 异常情况:

StackOverflowError:线程请求的栈深度大于虚拟机所允许的最大深度。一般遇到的都是这种状况。

OutOfMemoryError:虚拟机在扩展栈时无法申请到足够的内存空间。

如果因为建立过多线程导致内存溢出,在不能减少线程数或者更换64位虚拟机的情况下,就只能通过减少最大堆和减少栈容量来换取更多的线程。

 

本地方法栈

本地方法栈(Native Method Stack)被虚拟机用来支持用到的Native方法。在虚拟机规范中对本地方法栈中方法使用的语言、使用方式与数据结构并没有强制规定。HotSpot直接把本地方法栈和虚拟机栈合二为一了。

异常情况:与虚拟机栈一样,也会抛出StackOverflowError和OutOfMemoryError异常。

 

Java

对于大多数应用,Java堆(heap)是被所有线程共享的一块内存区域。一般来说,堆是Java放置对象实例的主要区域。但是,随着JIT编译技术的发展,栈上分配和标量替换优化技术(需要逃逸分析技术的支持)也使这种分配方式变得不那么绝对了。Java堆是垃圾回收的主要区域(方法区也会回收一些不用的常量等)。Java堆可以细分为:新生代、老年代、永久代。堆中储存的对象被自动管理内存系统(Automatic Storage Management System,也即是常说的“Garbage Collector(垃圾回收器)”)所管理,所以Java程序员无需、也无法显示地销毁对象。Java堆的容量可以是固定大小,也可以随着需求动态扩展(通过-Xmx和-Xms控制)。Java堆所使用的内存不需要保证是物理连续的,只要逻辑上是连续的即可。

Java 堆异常:

OutOfMemoryError:如果在堆中没有内存完成实例分配,并且堆也无法再扩展时,抛出异常。

 

方法区(Method Area)

方法区(Method Area)与Java堆一样,是各个线程共享的内存区域,被用来存储已被类加载子系统加载的类信息、常量、静态变量、即时编译器编译后的代码等数据。方法区的容量可以是固定大小的,也可以随着程序执行的需求动态扩展,并在不需要过多空间时自动收缩。方法区在实际内存空间中可以是不连续的。

Java 方法区异常:

OutOfMemoryError: 方法区的内存空间不能满足内存分配请求。

方法区溢出也是一种常见的内存溢出异常。在经常动态生成大量Class的应用中,需要特别注意类的回收状况。常见的情景有:大量JSP或动态产生JSP文件的应用、基于OSGi的应用等。

 

运行时常量池(Runtime Constant Pool

运行时常量池是方法区的一部分。运行时常量池用于存放编译器生成的各种字面量和符号引用,这部分内容将在类加载后进入运行时常量池中存放。

在扩展类和接口的运行时常量池时,可能会遇到的异常:

OutOfMemoryError:和方法区一样,也会抛出OutOfMemoryError异常。

 

直接内存(Direct Memory

NIO引入了基于通道和缓冲区的I/O方式,它可以使用Native函数分配堆外内存,然后通过一个存储在Java堆中的DirectByteBuffer对象作为这块内存的引用进行操作。