当前位置:网站首页>JVM 内存管理 你知道多少
JVM 内存管理 你知道多少
2022-07-28 12:06:00 【daydreamed】
JVM 内存管理 你知道多少
1、概述
“Java 虚拟机在执行 Java 程序的过程中会把它所管理的内存划分为若干个不同的数据区域。这些区域有各自的用途,以及创建和销毁的时间,有的区域随着虚拟机进程的启动而一直存在,有的区域则是依赖用户线程的启动和结束而建立和销毁。”
Java 虚拟机运行时的数据区:
补充:
线程隔离的数据区:
- 虚拟机栈(VM Stack)
- 本地方法栈(Native Method Stack)
- 程序计数器(Program Counter Register)
线程共享的数据区:
- 方法区(Method Area)
- 堆(Heap)
2、Java 内存区域
程序计数器(Program Counter Register)
程序计数器 是 用于存放下一条(Java 虚拟机)字节码指令所在单元的地址 的地方,该区域所在的内存 属于 “线程私有”内存。
如果 线程 正在执行的是一个 Java 方法,这个计数器的值为正在执行的 (Java 虚拟机)字节码指令的地址;如果 线程 正在执行的是本地(Native)方法,则这个计数器的值为空。
虚拟机栈(VM Stack)
虚拟机栈 是 用于存放 栈帧(Stack Frame) 的地方,每当一个(Java)方法被执行时,Java 虚拟机 就会创建一个 栈帧,栈帧 存储着 局部变量表、操作数栈、动态连接、方法出口等信息,一个(Java)方法 从 调用 到 执行完毕 的过程 对应着 栈帧 入栈 到 出栈 的过程,该区域所在的内存 属于 “线程私有” 内存。
本地方法栈(Native Method Stack)
本地方法栈 的功能与 虚拟机栈 相似,唯一不同的是 虚拟机栈 服务于 虚拟机执行 Java 方法,本地方法栈 服务于 虚拟机执行本地方法,该区域所在的内存 属于 “线程私有” 内存。
堆(Heap)
堆 是 用于存放(Java)对象实例 的地方,该区域所在的内存 属于 “线程共享” 内存。
堆 是 垃圾收集器 管理的内存区域,因此也被称为 “GC堆”。
堆的大小 可以通过 (Java 虚拟机)参数 -Xmx 和 -Xms 进行设定。
方法区(Method Area)
方法区 是用于存放 被虚拟机加载的类型信息、常量、静态变量、即时编译器编译后的代码缓存等数据 的地方,该区域所在内存 属于 “线程共享” 内存。
扩展:
运行时常量池(Runtime Constant Pool)
运行时常量池 是 方法区 的一部分,用于存放 编译期生成的各种 字面量 与 符号引用。
直接内存(Direct Memory)
直接内存 并不属于 Java 内存区域,但这部分内存也被频繁地使用到,例如:通过 Native 函数库直接分配 堆外内存,减少数据传输损耗,提高性能。
直接内存 只受本机内存限制。
3、Java 对象
3.1、对象的创建
在 Java 语言中,创建一个对象仅仅需要一个关键字 new ,但在 Java 虚拟机 中,创建一个对象则需要经过以下几个步骤。
1、当 Java 虚拟机 遇到一条字节码 new 指令时,首先会去检查 这个指令的参数 是否能在 常量池 中定位到 一个类的符号引用,并且检查这个 符号引用代表的类 是否 已被加载、解析和初始化过,如果没有就先执行 该类的类加载过程。
2、在类加载检查通过后,虚拟机 通过 “指针碰撞”(或 “空闲列表”)分配方式 在 堆 中为 该对象分配内存空间。
3、内存空间分配完成之后,虚拟机 必须将 分配到的内存空间(但不包括对象头)都初始化为 零值。
4、接下来,虚拟机 还将对 对象 进行必要的设置,如:该对象是哪个类的实例、如何才能找到类的元数据信息、对象的哈希码(延后至 Object:hashCode() 的调用 才进行计算)、对象的 GC 分代年龄等,这些信息均存放于 对象的对象头中。
5、至此,从虚拟机的角度来看,一个新的对象已经诞生,但从 Java 程序的角度来看,对象的创建才刚刚开始(构造函数还没有执行),接下来即是 执行构造函数(在虚拟机中体现为 <init>() 方法)完成对 对象的初始化,正式完成对 对象的构造。
3.2、对象的内存布局
对象 在 堆 中的内存布局可以划分为三个部分:
- 对象头(Header)
- 对象自身的运行数据:哈希码、GC 分代年龄、锁状态标志等
- 类型指针:指向 对象的类型元数据(即 是哪个类的实例)
- 实例数据(Instance Data)
- 字段内容
- 对其补充(Padding)
- 占位符
3.3、对象的访问定位
Java 程序在使用对象时,会通过 栈 上的 reference 数据来操作 堆 中的具体对象,而 reference(引用) 访问定位 堆 中的对象可分两种方式:
句柄访问
堆 中划分出一块内存作为句柄池(句柄包含 对象实例数据地址 与 类型数据地址),reference 中存储的就是 对象的句柄地址。
优势:reference 中存储的是稳定的句柄地址,在对象被移动时只会改变句柄中的实例数据指针。

直接指针访问
reference 中直接存储 对象地址。

优势:相较于句柄方式 节省了一次指针定位的时间开销,提高了访问速度。
3.4、对象的引用
强引用(Strongly Reference)
指 在程序代码中的引用赋值(
Object obj = new Object()),只要 强引用关系 还存在,垃圾收集器 就永远不会回收掉 被引用的对象。软引用(Soft Reference)
指 一些还有用、但非必须的对象,在系统将要发生 内存溢出异常 前,垃圾收集器 会将这些对象进行回收。
JDK1.2 后,提供 SoftReference 类 实现 软引用。
弱引用(Weak Reference)
指 一些非必须的对象,当 垃圾收集器 开始工作时,无论当前内存是否充足,都会将这些对象进行回收。
JDK1.2 后,提供 WeakReference 类 实现 弱引用。
虚引用(Phantom Reference,又称 “幽灵引用” 或 “幻影引用”)
一个对象是否有虚引用的存在,完全不会对其生存时间构成影响,也无法通过虚引用来取得一个对象实例,它存在的唯一目的只是:为了能在这个对象被 垃圾收集器 回收时收到一个系统通知。
JDK1.2 后,提供 PhantomReference 类 实现 虚引用。
3.5、对象的存亡
当一个对象 “死亡” 时,GC(Garbage Collection,垃圾收集)会将该对象清除 并 回收分配给该对象的内存空间。那么,GC 怎么判断一个对象是否 “死亡” 了呢?
GC 判断一个对象是否 “死亡” 通常有以下两种算法:
引用计数算法
在 对象 中添加一个 引用计数器,每当有一个地方引用它时,计数器值加一;每当有一个地方的引用失效时,计数器值减一;任何时刻计数器值为 零 的对象就是 “已死亡” 的对象(即 该对象不可能再被使用)。
- 优点:原理简单,判断效率高
- 缺点:占用额外的内存空间、无法解决对象间的循环引用问题
可达性分析算法
以 “GC Roots”(根对象)作为起始节点集,从这些节点开始,根据引用关系向下搜索,搜索过程所走过的路径被称为 “引用链(Reference Chain)”,如果 某个对象 到 “GC Roots” 间没有任何 引用链 相连(或者 用图论的话来说就是 从 “GC Roots” 到 这个对象不可达)时,该对象 “已死亡”(即 该对象不可能再被使用)。
- 优点:解决了对象间的循环引用问题
- 缺点:相较于 引用计数算法 判断效率 没那么高
扩展:Java 中的 “GC Roots”
- 虚拟机栈(栈帧中的本地变量表)中 引用的对象,如:当前正在运行的方法 所使用到的 参数、局部变量、临时变量等
- 方法区中 类静态属性 引用的对象,如:Java类 的引用类型静态变量
- 方法区中 常量 引用的对象,如:字符串常量池里的引用
- 本地方法栈中 JNI(Native 方法)引用的对象
- Java 虚拟机 内部的引用,如:基本数据类型对象的 Class 对象、一些常驻的异常对象(NullPointException、OutOfMemoryError)、系统类加载器
- 所有被同步锁(synchronized 关键字)持有的对象
- 反映 Java 虚拟机 内部情况的 JMXBean、JVMTI 中注册的回调、本地代码缓存等
- 不同垃圾收集器 和 不同回收的内存区域 中的 “临时性” 对象

补充:
- 对象的 “缓刑”:
当一个对象在 可达性分析算法 中判定为 不可达对象时,该对象会进入 “缓刑” 阶段:检测该对象是否有必要执行 finalize() 方法。
如果对象没有覆盖 finalize() 方法(或 finalize() 方法已经被虚拟机调用过),则 虚拟机 认为该对象 没必要执行 finalize() 方法,真正宣布其 “死亡”;
否则,虚拟机 认为该对象 有必要执行 finalize() 方法,并将该对象放入 F-Queue 队列中,然后由虚拟机 自动建立的、低调度优先级 的 Finalizer 线程 去执行它的 finalize() 方法(这里的 “执行” 是指虚拟机会触发这个方法开始运行,但并不承诺一定会等待它运行结束)。
- 对象的 “重生”:
当对象在 finalize() 方法中 重新与引用链上的任何一个对象建立关联,如:把自己(this 关键字)赋值给某个类变量或者对象的成员变量,即 该对象获得 “重生”(移出 “即将回收” 的集合)。
相反,如果该对象在 finalize() 方法中 没能重新与引用链上的任何一个对象建立关联,该对象也将面临 “死亡”。
4、垃圾收集算法
4.1、分代收集理论
分代收集名为理论,实质是一套符合大多数程序运行实际情况的经验法则,它建立在两个分代假说和一条经验法则之上。
弱分代假说(Weak Generational Hypothesis):绝大多数对象都是朝生夕灭的
强分代假说(Strong Generational Hypothesis):熬过越多次垃圾收集过程的对象就越难以消亡
跨代引用假说(Intergenerational Reference Hypothesis):跨代引用相对于同代引用来说仅占极少数
基于 分代收集理论,垃圾收集器 一致的设计原则为:收集器应该将 Java 堆划分出不同的区域,然后将回收对象依据其年龄(即对象熬过垃圾收集过程的次数)分配到不同的区域之中存储。
补充:
Java 堆的分代
- 新生代(Young Generation)
- 老年代(Old Generation)
分代收集
- 部分收集(Partial GC):
- 新生代收集(Minor GC / Young GC):只对新生代进行垃圾收集
- 老年代收集(Major GC / Old GC):只对老年代进行垃圾收集
- 混合收集(Mixed GC):既对 整个新生代进行垃圾收集,也对 部分老年代进行垃圾收集
- 整堆收集(Full GC):对整个 Java 堆 和 方法区 进行垃圾收集
4.2、标记 - 清除算法
算法分为 “标记” 和 “清除” 两个阶段:首先标记出所有需要回收的对象,在标记完成之后,统一回收掉所有被标记的对象(或统一回收掉所有未被标记的对象)。
缺点:产生碎片空间。

4.3、标记 - 复制算法
将内存按容量划分为大小相等的两块,每次只使用其中的一块,当这一块的内存用完了,就将还存活着的对象复制到另一块上,然后再把已使用过的内存空间一次性清理掉。
缺点:可用内存减半。

4.4、标记 - 整理算法
算法的标记过程 与 “标记 - 清除算法” 一致,整理过程则是:将所有存活的对象都向内存空间一端移动,然后直接清理掉边界以外的内存。
缺点:移动 大对象 可能造成较久的停顿。

以上全部内容均来自于对 周志明老师的 《深入理解 Java 虚拟机》的部分内容的总结。
我的总结于这本书的内容相比,无亦于水滴同大川,真心推荐大家去细读这本书。
以上内容仅用于个人学习。
边栏推荐
- Phpstudy steps to quickly build a website (teach you to build it by hand)
- LeetCode·每日一题·1331.数组序号转换·离散化
- Jetpack 全家桶之 LiveData 使用及源码篇
- 【嵌入式C基础】第7篇:C语言流程控制详讲
- [embedded C foundation] Part 5: original code / inverse code / complement code
- Fundamentals of machine learning - principal component analysis pca-16
- Storage model: big end and small end
- Single option trading and cross trade
- Vs code is not in its original position after being updated
- Led aquarium lamp touch chip-dlt8t02s-jericho
猜你喜欢

CTO of youhaoda, MVP of Huawei cloud, and Zhang Shanyou: build cloud native applications based on kubernetes and dapr

Remove the plug-in of category in WordPress link
![[Bi design teaching] STM32 and FreeRTOS realize low power consumption](/img/2b/3af85135e08599aaa425698c0e83aa.png)
[Bi design teaching] STM32 and FreeRTOS realize low power consumption

Kotlin是如何帮助你避免内存泄漏的?

How to view win11 system and reserved space?
![[pictures and texts] detailed tutorial of one click reinstallation of win11 system](/img/cc/749fe4095fc5afb1fc2c65df43d06c.png)
[pictures and texts] detailed tutorial of one click reinstallation of win11 system

Fundamentals of machine learning Bayesian analysis-14

Vs code is not in its original position after being updated

企业数字化本质

Connected Block & food chain - (summary of parallel search set)
随机推荐
04 pyechars geographic chart (example code + effect diagram)
【嵌入式C基础】第6篇:超详细的常用的输入输出函数讲解
[embedded C foundation] Part 5: original code / inverse code / complement code
[pictures and texts] detailed tutorial of one click reinstallation of win11 system
Databinding+livedata can easily realize skin changing without restart
Automatic light sensing arm lamp touch chip-dlt8sa15b-jericho
管理区解耦架构见过吗?能帮客户搞定大难题的
How many times can the WordPress user name be changed? Attach the method of changing user name
Change password, confirm password verification antd
Unity—“合成大西瓜”小游戏笔记
Protobuf data exchange format
Kotlin是如何帮助你避免内存泄漏的?
Smart touch screen LCD bathroom mirror light touch chip-dlt8t02s-jericho
[FPGA]: ISE generates MCS file and burning process
Databinding+LiveData轻松实现无重启换肤
Full disclosure! Huawei cloud distributed cloud native technology and Practice
Le transaction
[FPGA]: Joint Simulation of FPGA and MATLAB
What if the win11 folder cannot be opened
黑猫带你学eMMC协议第24篇:eMMC的总线测试程序详解(CMD19 & CMD14)