
对于Java开发人员来说,了解Java中存储对象和变量的内存区域非常重要. 本文将全面解释Java虚拟机中的内存模型和分区.
Java虚拟机将托管内存划分为几个不同的数据区域,如下图所示.
内存模型
下面分别说明每个区域的功能.
1. Java堆

Java堆是由所有线程共享的内存区域,在虚拟机启动时创建. 该存储区的唯一目的是存储对象实例,并且所有对象实例和数组都必须在堆上分配. Java堆是由垃圾收集器管理的主要区域,因此通常称为“ GC堆”.
从内存恢复的角度来看,由于收集器现在基本上使用世代收集算法,因此Java堆也可以细分为年轻一代和老一代. 更详细的信息包括Eden空间,From Survivor空间和To Survivor Space等.
从内存分配的角度来看,可以将多个线程专用分配缓冲区(线程本地分配缓冲区,TLAB)划分为线程共享的Java堆.
根据Java虚拟机规范,Java堆可以在物理上不连续的内存空间中,只要它在逻辑上是连续的即可. 当实现时,它可以固定大小或可扩展方式实现,但是当前的主流虚拟机都是根据可伸缩性实现的.
如果堆中没有内存来完成实例分配,并且无法再扩展堆,则会引发OutOfMemoryError.
2. 方法区域

方法区域,就像Java堆一样,是所有线程共享的内存区域,用于存储虚拟机已加载的数据,例如类信息,常量,静态变量和JIT编译器编译的代码. 尽管Java虚拟机规范将方法区域描述为堆的逻辑部分,但它具有一个名为Non-Heap(非堆)的别名,以将其与Java堆区分开.
Java虚拟机规范对方法区域有非常宽松的限制. 除了与Java堆相同外,它不需要连续的内存,可以选择固定大小或可扩展性. 您也可以选择不实施垃圾收集. 相对而言,垃圾回收行为在该区域相对较少,但并不是数据作为永久代的名称以“永久”进入方法区域. 该区域的内存恢复目标主要是为了恢复常量池和卸载类型. 一般来说,该区域的恢复“分数”比较难以满足,尤其是卸货类型,条件相当苛刻,但是这部分区域的回收确实是必要的.
运行时常量池是方法区域的一部分,用于存储编译期间生成的各种文字常量和符号引用.
根据Java虚拟机规范,当方法区域不能满足内存分配要求时,将引发OutOfMemoryError异常.
3. Java虚拟机堆栈
Java虚拟机堆栈是线程专用的,其生命周期与线程的生命周期相同. 虚拟机堆栈描述了Java方法执行的内存模型: 执行每个方法时,将同时创建一个堆栈帧以存储诸如局部变量表,操作堆栈,动态链接和方法返回地址之类的信息. 每种方法从被调用到执行完成的过程,与将堆栈帧从推入到弹出到虚拟机堆栈中的过程相对应,如下图所示.

Java虚拟机堆栈
局部变量表用于存储方法参数和方法内部定义的局部变量. 局部变量的大小已在代码编译期间确定,并且在方法运行期间不会更改. 局部变量表使用Slot作为最小的存储单元. 每个插槽可以存储32位数据类型的布尔值,字节,字符,短型,整数,浮点型,引用(对象引用)和returnAddress(指向字节码指令的地址). 对于长和双精度的64位数据类型,虚拟机以高阶对齐方式分配两个连续的插槽空间.
在执行该方法时,如果它是一个实例方法(即非静态方法),则默认情况下,局部变量表中的第0个插槽存储对象实例的引用. 可以在方法中使用关键字this进行访问,并且方法参数按参数列表的顺序排列,分配从第一个插槽开始,并且该方法的内部变量按以下顺序分配给其余的插槽定义.
在Java虚拟机规范中,为此区域指定了两个异常: 如果线程请求的堆栈深度大于虚拟机允许的深度,则将引发StackOverflowError异常;否则,将抛出异常. 如果可以动态扩展虚拟机堆栈,则在无法申请足够的内存时将引发OutOfMemoryError.
4. 本地方法堆栈

本机方法堆栈和虚拟机堆栈所扮演的角色非常相似. 区别在于,虚拟机堆栈为虚拟机提供服务以执行Java方法(即字节码),而本地方法堆栈由虚拟机本机方法服务使用.
本地方法堆栈中方法的语言,用法和数据结构在虚拟机规范中不是强制性的,因此特定的虚拟机可以自由地实现它,甚至某些虚拟机(例如Sun HotSpot虚拟机)也可以直接实现. 将本地方法堆栈和虚拟机堆栈合并为一个.
与虚拟机堆栈一样,本地方法堆栈区域也会引发StackOverflowError和OutOfMemoryError异常.
5. 程序计数器
程序计数器可以看作是当前线程执行的字节码行号的指示器. 在虚拟机的概念模型中,当字节码解释器工作时,它通过更改该计数器的值来选择要执行的下一个字节码指令. 基本功能(例如分支,循环,跳转,异常处理和线程恢复)都需要依靠此计数器来完成.
由于Java虚拟机的多线程是通过线程交替切换和分配处理器执行时间的方式实现的,因此在任何时刻,处理器将仅在一个线程中执行指令. 因此,为了程切换后恢复到正确的执行位置,每个线程都需要有一个独立的程序计数器,并且线程之间的计数器不会互相影响,而是独立存储.
如果线程正在执行Java方法,则此计数器记录正在执行的虚拟机字节码指令的地址;如果执行的是本机方法,则计数器值为空(未定义).
此内存区域是Java虚拟机规范中唯一未指定任何OutOfMemoryError条件的区域.
本文来自电脑杂谈,转载请注明本文网址:
http://www.pc-fly.com/a/shoujiruanjian/article-307081-1.html
他们了解世界更了解我们大陆
)