开启掘金成长之旅!这是我参与「掘金日新计划 · 12 月更文挑战」的第27天,点击查看活动详情
一、Java 内存模型
1)、Java 内存模型,往往是指 Java 程序在运行时内存的模型,而 Java 代码是运行在 Java 虚拟机之上的,所以 Java 内存模型,也就是指 Java 虚拟机的运行时内存模型。
2)、Java 中内存全权交给 Java 虚拟机去管理,那么 Java 虚拟机的运行时内存是如何构成的呢?
如下图:
从上图可以看出 Java 虚拟机运行时内存划分为:
1、方法区,包含常量池
2、Java 堆
3、程序计数器
4、Java 虚拟机栈
5、本地方法栈
其中,程序计数器,Java 虚拟机栈和本地方法栈是线程私有的。方法区和 Java 堆是线程共享的。下面对他们进行详细的介绍:
1.1、方法区
作用:存储已经被 Java 虚拟机加载的类的结构信息,包括运行时常量池、字段和方法信息、静态变量等数据。
范围:被所有线程共享
异常:OutOfMemoryError 异常,在方法区的内存空间不满足内存分配需求时,会抛出。
1.1.1、常量池
作用:用于存放编译器生成的各种字面量和符号引用。运行时常量池除了编译期产生的 class 文件的常量池,还可以在运行期间,将新的常量加入常量池,比如 String 类的 intern() 方法。
范围:运行时常量池是方法区的一部分
异常:OutOfMemoryError 异常
1.2、Java 堆
作用:Java 堆用来存放对象实例,几乎所有的对象实例都在这里分配内存。而在虚拟机栈中分配的只是引用,这些引用会指向堆中真正存储的对象。
范围:被所有线程共享
异常:OutOfMemoryError异常,在堆中没有足够的内存来完成实例分配,并且堆也无法进行扩展时,则会抛出。
1.3、程序计数器
作用:记录线程代码执行到哪个位置
范围:线程私有
异常:程序计数器是 Java 虚拟机规范中唯一没有规定任何 OutOfMemoryError 情况的数据区域。
1.4、Java 虚拟机栈
作用:存储线程中 Java 方法调用的状态,包括局部变量、参数、返回值以及运算的中间结果等。一个 Java 虚拟机栈包含了多个栈帧,一个栈帧用来存储:局部变量表、操作数栈、动态链接、方法出口等信息。当线程调用一个 Java 方法时,虚拟机压入一个新的栈帧到该线程的 Java 虚拟机栈中,在该方法执行完成后,这个栈帧就从 Java 虚拟机栈中弹出。我们平常所说的栈内存(Stack)指的就是Java虚拟机栈。
范围:线程私有,生命周期与线程相同,与线程是同时创建。
异常:
StackOverFlowError:当线程请求栈深度超出虚拟机栈所允许的深度时抛出 (递归函数);
OutOfMemoryError:当Java虚拟机动态扩展到无法申请足够内存时抛出 (OOM)
1.5、本地方法栈
作用:作用同虚拟机栈,只不过本地方法栈是为native方法服务的。
范围:线程私有
异常:StackOverflowError 和 OutOfMemoryError 异常
1.6、关于内存使用经验之谈
1)、局部变量的基本数据类型和引用存储于栈中,引用的对象实体存储于堆中。因为它们属于方法中的变量,生命周期随方法而结束。
2)、成员变量全部存储于堆中(包括基本数据类型,引用和引用的对象实体)。因为它们属于类,类对象终究是要被new出来使用的。
3)、我们知道,内存的释放是由垃圾收集器(Garbage Collection,GC)完成的,而 GC 主要就是针对Java 堆内存进行管理和回收,但 GC 只能回收无用并且不再被其它对象引用的那些对象所占用的堆空间,并且内存泄漏也都是发生在这个区域。
4)、堆中几乎存放着 Java 世界中所有的对象实例,垃圾收集器在对堆回收之前,第一件事情就是要确定这些对象哪些还“存活”着,哪些对象已经“死去”(即不可能再被任何途径使用的对象),确定对象是否存活这就需要了解:垃圾标记算法,这个后续在讲。
二、总结
本篇文章我们介绍了:
1、Java 虚拟机运行时内存模型,主要分为 5 大块:
1、方法区,包含常量池
2、Java 堆
3、程序计数器
4、Java 虚拟机栈
5、本地方法栈
2、详细讲了 5 大块内存的作用,范围,以及异常
3、介绍了内存使用过程中一些经验之谈
好了,本篇文章到这里就结束了,感谢你的阅读🤝