Java虚拟机核心知识(二) JVM内存模型

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/u013667756/article/details/82936591

说起内存,大家很容易就想到了内存溢出,对于Java工程狮来说,遇到OutOfMemory可以说是家常便饭了。在学习如何定位这些异常发生的原因并提出解决方案之前,我们必须了解一下,Java虚拟机是如何划分自己的内存区域的。

一. Java内存区域

一个Java进程启动后,会被划分一块类似于疆土的内存区域,虚拟机将这块大的内存,按照所存储的数据类型,划分为不同的区域进行管理,Java虚拟机的运行时数据区,可以用下面这张图来表示:

在这里插入图片描述
下面就来对这张内存区域模型图进行详细的讲解。

程序计数器

程序计数器在虚拟机内存中是一块很小的区域,这个计数器记录了当前线程执行到了哪一行的字节码指令。每条线程都拥有一个独立的程序计数器,有了这个计数器,才能在不停的线程切换中,让线程记得下一条要执行的指令。

程序计数器是唯一一个在Java虚拟机规范中没有规定任何OutOfMemory异常的区域。

Java虚拟机栈

和程序计数器一样,Java虚拟机栈也是线程私有的。虚拟机栈主要存储的内容是方法执行时的局部变量表,包括各种基本数据类型(boolean、byte、char、short、int、float、long、double)、对象引用(指向一个对象地址的指针)和returnAddress类型(指向一条字节码指令的地址)。

其中,64位长度的long和double会占用2个局部变量空间,其余数据类型只占用1个。局部变量表所需的内存空间在编译器就分配完成,也就是说,进入一个方法时,这个方法需要分配多大的局部变量空间是完全确定的。
在这里插入图片描述
每个线程都拥有一个虚拟机栈,线程中每个方法执行时都会在栈中创建一个栈帧,栈帧中就包含了上面所说的数据,方法每递归一次,就新建一个栈帧,依此类推。

在Java虚拟机规范中,这个区域有两种异常情况:

(1) 如果线程运行时的栈帧总得大小超过虚拟机限制的大小,会抛出StackOverflow异常,这一点通常发生在递归运行时。

(2) 如果虚拟机栈设置为可以动态扩展,并且在扩展时无法申请到足够内存,则会抛出OutOfMemory异常。

本地方法栈

本地方法栈发挥的作用和虚拟机栈是十分相似的,主要都是存储着方法执行的局部变量的信息,不同的是虚拟机栈是为执行Java方法(也就是字节码)服务,而本地方法栈是为虚拟机使用到的Native方法服务。

基于两者的相似性,HotSpot虚拟机直接将两者合二为一。

和虚拟机栈一样,本地方法栈也有StackOverflowOutOfMemory异常。

Java堆

Java堆是Java虚拟机内存中最大的一块,是被所有线程共享的一块内存区域。Java堆负责存放Java对象实例,因此Java堆也是垃圾收集的主要区域。

Java堆可以处于物理上不连续的内存空间中,只要逻辑上是连续的即可。

可以通过-Xmx和-Xms参数来控制堆的大小,当堆没有内存完成实例分配,且无法继续扩展时,就会抛出OutOfMemory异常。

方法区

这里的方法区用于存储已被虚拟机加载的类信息、常量、静态变量、JIT编译器编译后的代码等数据,也是各个线程共享的内存区域。

直接内存

直接内存并不是虚拟机运行时数据区的一部分,但是Java可以对这部分的内存进行使用,比如JDK 1.4新加入的NIO,它可以通过Native函数库直接在机器内存中获取内存,然后通过存储在Java堆中的对象,作为这块内存的引用进行操作,避免了在Java堆Native之间来回复制数据,提高了操作的性能。

直接内存的大小只受到本机总内存的限制,因此,如果直接内存使用过多,超过了机器的物理内存限制,就会导致OutOfMemory异常。

参考资料

《深入理解Java虚拟机》 周志明

猜你喜欢

转载自blog.csdn.net/u013667756/article/details/82936591
今日推荐