摘要:作为一个程序员,不了解内存模型就不能写出能够充分利用内存的代码。程序计数器是在电脑处理器中的一个寄存器,用来指示电脑下一步要运行的指令序列。在虚拟机中,本地方法栈和虚拟机栈是共用同一块内存的,不做具体区分。
作为一个 Java 程序员,不了解 Java 内存模型就不能写出能够充分利用内存的代码。本文通过对 Java 内存模型的介绍,让读者能够了解 Java 的内存的分配情况,适合 Java 初学者或者对 JMM 不熟悉的同学。后面的博客会针对每个部分做更加深入的解释。
Java 内存模型首先通过下图对于 Java 内存模型有一个整体的认识,然后针对不同的区域的作用和存储的内容做进一步的解释。
PC(程序计数器)这里的 PC 不是 Personal Computer,而是 Program Counter Register,从名字就可以看出来,这是一个寄存器,用来存储需要执行的指令地址。
</>复制代码
程序计数器(Program Counter (PC))是在电脑处理器中的一个寄存器,用来指示电脑下一步要运行的指令序列。--WikiPedia
PC 和其他 JVM 内存区域最大的区别是:
</>复制代码
“此内存区域是唯一一个在Java虚拟机规范中没有规定任何OutOfMemoryError情况的区域。”
摘录来自: 周志明. “深入理解Java虚拟机:JVM高级特性与最佳实践(第2版)”。 iBooks.
像上面的图片一样,PC 是每个线程私有的,对于 Java 方法而言,PC 中存储的是正在执行的虚拟机字节码的内存地址;对于 Native 方法来说,PC 中的值为空(Undefined)。
Java 虚拟机栈和本地方法栈 虚拟机栈无论是在大学的 Java 编程课堂上,还是我们在学习过程编码过程,经常会出现 StackOverFlow,甚至目前最大的技术问答社区的名字也是 StackOverFlow。Java 语言中会产生栈溢出的就是这块内存区域,当你的程序中设置了超过 JVM 规定的递归深度的时候就会触发这个异常。类似 JMM 的其他内存区域,如果虚拟机栈在动态扩展的时候无法申请到足够的内存也会报OOM异常。
Java 语言中每一个方法的执行都对应着一个栈帧(Stack Frame)的创建,栈帧中存储的是局部变量、方法出口等信息,因此对于一个方法的执行而言,所能够使用到的内存是在编译期间就能够完全确定的,在运行期间不会发生变化。在栈帧中,局部变量空间成为 Slot,除了 double 和 long 占有 2 个 slot 外,其他基本数据类型和对象引用都占用 1 个 slot 空间
本地方法栈本地方法栈和虚拟机栈最大的区别就是虚拟机栈是为执行 Java 字节码服务的,而本地方法栈是为了虚拟机使用到的 Native 方法服务的。除此之外,Java 虚拟机规范并没有针对本地方法栈的实现做具体规定。在 HotSpot 虚拟机中,本地方法栈和虚拟机栈是共用同一块内存的,不做具体区分。同样,本地方法栈也会产生 OOM 异常和 StackOverFlow 异常。
Java 堆</>复制代码
“The heap is the runtime data area from which memory for all class instances and arrays is allocated。” --Java虚拟机规范
Java 虚拟机规范规定所有的实例对象和数组都应该分配到 Java 堆中。
说的通俗一点就是所有 new 出来的对象和数组都会放到该区域,由于现在的收集器都采用分代收集算法,所以在 Java 堆中又分了新生代和老年代,新生代有做了详细的区分。该区域的大小可以通过 JVM 参数 -Xmx 和 -Xms 来设置。
在 JDK1.4 中引入了 NIO,可以通过 Native 方法直接在堆外分配内存,然后通过在堆中存储的引用来对这块内存区域做操作。注意 这块区域并不会在 -Xmx和 -Xms 设置的大小之内,因此在设置 JVM 参数的时候要注意考虑这块内存区域,避免设置的内存区域总额大于物理内存
方法区Method Area 又叫 NonHeap,也是线程共有的内存区域,用来存:
类信息
常量
静态变量
字符串常量池
在 JDK1.7 中已经将字符串常量池移出永久代,在 Java8 中更是之内取消了永久代,而是使用了元空间(MetaSpace)来存储这些信息,从而永久代的大小不需要再制定,只要不超出物理内存的限制就不会产生 OOM 异常
运行时常量池运行时常量池主要用来存储累的版本、字段、方法、接口等描述信息。常量池(Constant Pool Table)用来存储各种字面量和符号引用。String 的 intern() 方法就是在运行期间将对象放到常量池中的。此部分也会出现 OOM 异常。
文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。
转载请注明本文地址:https://www.ucloud.cn/yun/69540.html
摘要:内存模型首先介绍下程序具体执行的过程源代码文件后缀会被编译器编译为字节码文件后缀由中的类加载器加载各个类的字节码文件,加载完毕之后,交由执行引擎执行在整个程序执行过程中,会用一段空间来存储程序执行期间需要用到的数据和相关信息,这段空间一般被 [TOC] JVM内存模型 首先介绍下Java程序具体执行的过程: Java源代码文件(.java后缀)会被Java编译器编译为字节码文件(....
摘要:基础问题的的性能及原理之区别详解备忘笔记深入理解流水线抽象关键字修饰符知识点总结必看篇中的关键字解析回调机制解读抽象类与三大特征时间和时间戳的相互转换为什么要使用内部类对象锁和类锁的区别,,优缺点及比较提高篇八详解内部类单例模式和 Java基础问题 String的+的性能及原理 java之yield(),sleep(),wait()区别详解-备忘笔记 深入理解Java Stream流水...
摘要:基础问题的的性能及原理之区别详解备忘笔记深入理解流水线抽象关键字修饰符知识点总结必看篇中的关键字解析回调机制解读抽象类与三大特征时间和时间戳的相互转换为什么要使用内部类对象锁和类锁的区别,,优缺点及比较提高篇八详解内部类单例模式和 Java基础问题 String的+的性能及原理 java之yield(),sleep(),wait()区别详解-备忘笔记 深入理解Java Stream流水...
阅读 4592·2021-11-24 10:24
阅读 1490·2021-11-22 15:22
阅读 2223·2021-11-17 09:33
阅读 2585·2021-09-22 15:29
阅读 581·2019-08-30 15:55
阅读 1729·2019-08-29 18:42
阅读 2803·2019-08-29 12:55
阅读 1852·2019-08-26 13:55