版权申明:转载请注明出处。
文章来源:玄涧思库
jvm在运行时会将管理的内存划分为多个区域,每个区域都有各自的作用,而且几乎每个区域都会有内存溢出或者泄露的问题。先上一张jvm运行时数据区域图。
1.程序计数器
程序计数器是记录线程执行的字节码所在行号的指示器,其在内存中占用一小块空间。机器执行指令时,任何时候都只能执行一条指令,对于多核处理器也是如此。字节码解释器需要通过改变程序计数器的值来确定下一条执行的指令。为了确保线程切换后能回到正确的执行位置,每个线程都需要有自己独立的程序计数器。如果线程正在执行一个java方法,程序计数器的值是虚拟机字节码的地址,如果执行的是一个native方法,则程序计数器的值为空。
2.Java虚拟机栈
Java虚拟机栈是线程私有的,它的生命周期与线程的生命周期一样,每个方法执行的时候都会创建一个栈,这个栈用来存放局部变量表、操作数栈、动态链接、方法出口等信息。
其中局部变量表用来存放编译期可知的各种基本数据类型(boolean、byte、char、short、int、float、long、double)、对象引用和returnAddress类型。
在java虚拟机规范中,对这个区域的异常做了两种说明:1.如果线程申请的栈深度大于虚拟机允许的栈深度,将会抛出StackOverflowError。2.如果虚拟机栈可以动态扩展,而扩展时无法申请到足够的内存,将抛出OutOfMemoryError。
3.本地方法栈
本地方法栈与虚拟机栈非常相似,只不过本地方法栈用来执行本地方法,虚拟机栈用来执行java方法。对于本地方法使用何种语言,java虚拟机规范没有做强制约束,因此各虚拟机厂商可以自由实现。本地方法栈和java虚拟机栈一样也会抛出StackOverflowError以及OutOfMemoryError。
4.Java堆
java堆是被所有线程共享的区域,也是java内存管理中最大的一块。它最主要的目的是存放对象实例,几乎所有的对象都在这里分配内存。
java堆被分为很多区域(eden、from survivor、to survivor、老年代等)是因为现在的垃圾回收器都是基于分代收集算法,所以这个区域划分是站在垃圾回收器的角度来看的。
java虚拟规范中指出,java堆可以处于物理上不连续的内存空间中,只要逻辑上连续就行。目前主流的虚拟机的堆都是按照可自动扩展来实现的,若扩展失败会抛出OutOfMemoryError。
5.方法区
方法区也是各个线程共享的区域,用于存储已被虚拟机加载的类信息、常量、静态变量、即时编译器编译后的代码等数据。java虚拟机规范对这个区域的限定非常宽松,除了可以使用不连续的存储空间、可以定义固定或者可动态扩展的的大小,还可以不实现垃圾回收器。
6.运行时常量池
运行时常量池是方法区的一部分。Class文件除了有类的版本、字段、方法、接口等描述信息外,还有一项信息是常量池。用于存放编译初期生成的各种字面量和符号引用,这部分内容将在类加载后进入方法区的运行时常量池中存放。运行时常量池对于Class文件常量池的另一个重要的特性是具备动态性,即在运行期间可以将新的常量放入常量池中。典型的有String的intern()方法。
7.直接内存
直接内存独立于虚拟机运行时数据区,也不属于java虚拟机中定义的区域。直接内存一般用于IO传输,它不经过java的堆内存,能显著的提高传输的效率。