本文从概念上介绍 Java 虚拟机内存的各个区域,讲解这些区域的作用、服务对象以及其中可能产生的问题。Java 虚拟机在执行 Java 程序的过程中会把它所管理的内存划分为若干个不同的数据区域。这些区域有各自的用途,以及创建和销毁的时间,有些区域随着虚拟机进程的启动而一直存在,有些区域则是依赖用户线程的启动和结束而建立和销毁。
“概念模型”这个词会经常被提及,它代表了所有虚拟机的统一外观,但各款具体的 Java 虚拟机并不一定要完全照着概念模型的定义来进行设计,具体的 Java 虚拟机可能会通过一些更高效率的等价方式去实现它。由于 Java 虚拟机的多线程是通过线程轮流切换、分配处理器执行时间的方式来实现的,在任何一个确定的时刻, 一个处理器(对于多核处理器来说是一个内核)都只会执行一个线程中的指令。因此,为了线程切换后能恢复到正确的执行位置,每个线程都需要有一个独立的程序计数器,各个线程之间的程序计数器互不影响,独立存储,我们称这类内存区域为 “线程私有” 的内存。
请读者注意,这里说的 “大小” 指的是变量槽的数量,虚拟机真正使用多大的内存空间(譬如按照 1 个变量槽占用 32 个比特、 64 个比特, 或者更多)来实现一个变量槽,这是完全由具体的虚拟机实现自行决定的事情。在《Java 虚拟机规范》中, 对 Java 虚拟机栈内存区域规定了两类异常状况:StackOverflowError、OutOfMemoryError
通过参数 -Xss 来设定单个线程栈的大小,栈的大小直接决定了函数调用的最大深度。HotSpot 虚拟机的栈容量是不可以动态扩展的,以前的 Classic 虚拟机倒是可以。所以在 HotSpot 虚拟机上是不会由于虚拟机栈无法扩展而导致 OutOfMemoryError 异常。只要线程申请栈空间成功了就不会有 OOM,但是如果线程申请栈空间失败了,仍然是会出现 OOM 异常的。
在《Java 虚拟机规范》中对 Java 堆的描述是:“所有的对象实例以及数组都应当在堆上分配”,而这里笔者写的“几乎”是指从实现角度来看,随着 Java 语言的发展,现在已经能看到些许迹象表明日后可能出现值类型的支持,即使只考虑现在,由于即时编译技术的进步,尤其是逃逸分析技术的日渐强大,栈上分配、标量替换优化手段已经导致一些微妙的变化悄然发生,所以说 Java 对象实例都分配在堆上也渐渐变得不是那么绝对了。根据《Java 虚拟机规范》的规定,Java 堆可以处于物理上不连续的内存空间中,但在逻辑上它应该被视为连续的,这点就像我们用磁盘空间去存储文件一样,并不要求每个文件都连续存放。但对于大对象(典型的如数组对象),多数虚拟机实现出于实现简单、存储高效的考虑,很可能会要求连续的内存空间。
固定大小的 Java 堆指的是:只在虚拟机启动时,向操作系统申请固定大小的堆内存空间。方法区
可扩展的 Java 堆指的是:在虚拟机启动时,向操作系统申请固定大小的初始堆内存空间。在空闲的 Java 堆内存空间无法满足新的内存分配需求时,再向操作系统申请堆内存空间。
欢迎光临 ToB企服应用市场:ToB评测及商务社交产业平台 (https://dis.qidao123.com/) | Powered by Discuz! X3.4 |