Java虚拟机运行时数据区域

Java虚拟机运行时数据区域_第1张图片

Java运行时的数据区和内存溢出简介
1.程序计数器
可以看做当前线程程序所执行字节码的行号指示器,字节码解释器工作时就是通过改变这个计数器的值来获取下一条需要执行的字节码指令的。每条线程都需要一个独立的程序计数器,各条线程之间的计数器互不影响,独立存储,被称为“线程私有”的内存。
该区域是唯一一个在Java虚拟机规范中没有规定任何OutofMemoryError情况的区域。

2.Java虚拟机栈
Java虚拟机栈(Stack)也是线程私有内存,其生命周期与线程相同,statck描述的是Java方法执行的内存模型,每个方法在执行的同时会创建一个栈帧(stack frame)用于存储局部变量表,操作数栈,动态链接方法出口等信息。每个方法从调用到执行完成,就对应一个栈帧在是statck中从入栈到出栈的过程。
局部变量表:存放了编译期可知的各种基本烈性,对象引用(引用指针,句柄),return address类型(字节码指令的地址)。局部变量表所需的内存空间是编译时确定的,进入某方法时,在帧中需要分配多大的句柄空间是确定的,且运行期间是不会改变的。
两种异常:如果线程请求的栈深度大于虚拟机所允许的深度将抛出,StackOverFlowError异常;如果虚拟机栈可以动态扩展,在扩展时无法申请到足够的内存,就好抛出OutofMemoryError异常。

3.本地方法栈
该区域与Java虚拟机栈类似,不同的是Java虚拟机栈执行的是Java方法,也就是字节码,而本地方法栈中执行的是native方法,虚拟机规范中,对于本地方法栈中的语言和数据结构没有强制规定。

4.Java堆
Java堆是被所有线程共享的一块内存区域,在虚拟机启动时创建,所有的对象实例以及数组都要在堆上分配内存,栈上分配、标量替换优化技术使得堆分配不再绝对。
Java堆是垃圾收集器管理的主要区域,因此也成为“GC堆”。由于现在垃圾收集器基本都采用分带手机算法,所以一般Java堆中还可以细分为新生代和老年代,再细致一点的还有Eden空间、from survivor空间、to survivor空间,从内存角度看还有划分出多个线程私有分配缓存区,TLAB,而这些划分与存放内容无关,无论哪个区域存放的都是对象实例。
如果堆中没有内存完成实例分配,也无法进行扩展就会抛出OutofMemoryError异常。

5.方法区
与Java堆一样,也是各线程共享的内存区域,用于存储虚拟机加载的类信息、常量、静态变量、即时编译器编译后的代码等数据,别名Non-heap,非堆,用以与Java堆区别。
对于HotSpot虚拟机而言,很多人将方法区称为持久代,本质上两者并不等价,只是因为HotSpot虚拟机使用永久代实现方法区而已,但是该方案现在看来并不是很好,这样的方式更容易导致内存溢出。在jdk1.7以后的版本,已经使用Native memory来实现方法区。Java虚拟机规范对这部分的限制非常宽松,除了可以使用不连续的空间内存和可以固定大小或者扩展的外,还可以选择不实现垃圾收集,这个区域的内存回收目标主要是针对常量池的回收和对类型的卸载。
当方法区无法满足内存分配时,会抛出OutOfMemoryError异常。

6.运行时常量池
运行时常量池(Runtime constant pool)是方法区的一部分,常量池(Constant Pool Table) ,用于存放编译期生成的各种字面量和符号引用。这部分内容将在类加载后进入方法区的运行时常 量池中存放.
运行时常量池相对于Class文件常量池一个重要特性就是具备动态性,Java允许在运行期间将新的常量加入常量池,比较典型的是String类的intern()方法。当常量池无法满足内存分配时,会抛出OutOfMemoryError异常。

7.直接内存
直接内存(Direct Memory)并不是虚拟机运行时数据区的一部分,也不是Java虚拟机规范中定义的内存区域,但是这部分内存使用比较频繁,而且也能够导致OutOfMemoryError异常。
在JDK 1 .4中新加入了NIO(New Input/Output) 盘,引入了一种基于通道(Channel)与缓冲区(Buffer) 的I/O方式,它可以使用Native函数库直接分配堆外内存, 然后通过一个存储在Java 堆中的DirectByteBufTer 对象作为这块内存的引用进行操作. 这样能在些场景中显著提高性能, 因为避免了在Java 堆和Nat ive 堆中来回复制数据。
该内存不会受到Java堆大小的限制,但是会受到本机总内存和cpu寻址的空间的限制。

你可能感兴趣的:(java,虚拟机,内存,内存溢出,java)