跳至主要內容

EkkoSonya's Blog

好好学习,天天向上

JVM 八股5 - 内存管理4 (对象内存布局)

对象内存布局

对象的内存布局是由 Java 虚拟机规范定义的,但具体的实现细节各有不同,如 HotSpot 和 OpenJ9 就不一样

常用的 HotSpot 为例:

对象在内存中包括三部分:对象头、实例数据和对齐填充

对象头 (Mark World + 类型指针 (指向 Class 对象)) + 实例数据 + 对齐填充

对象头 (8字节 + 8字节 (压缩 4 字节)): 12字节 (指针压缩)

对齐填充必须是 8 的倍数 (64位)

alt text

codejavajvm八股大约 5 分钟
JVM 八股6 - 内存管理5 (java 堆介绍)

Java 堆 (存放对象位置)

堆内存分区

Java 堆被划分为新生代和老年代两个区域

新生代又被划分为 Eden 空间和两个 Survivor 空间(From 和 To)

新创建的对象会被分配到 Eden 空间

当 Eden 区填满时,会触发一次 Minor GC,清除不再使用的对象

存活下来的对象会从 Eden 区移动到 Survivor 区。

对象在新生代中经历多次 GC 后,如果仍然存活,会被移动到老年代

当老年代内存不足时,会触发 Major GC,对整个堆进行垃圾回收


codejavajvm八股大约 5 分钟
JVM 八股7 - 内存管理6 (java 堆介绍)

内存溢出和内存泄漏

内存溢出

内存溢出,俗称 OOM,是指当程序请求分配内存时,由于没有足够的内存空间,从而抛出 OutOfMemoryError

List<String> list = new ArrayList<>();
while (true) {
  list.add("OutOfMemory".repeat(1000)); // 无限增加内存
}

codejavajvm八股大约 4 分钟
JVM 八股8 - 垃圾收集1

垃圾收集机制

垃圾回收就是对堆内存中不再被引用的(不可达的)对象进行清除或回收

JVM 在做 GC 之前,会先搞清楚什么是垃圾,什么不是垃圾,通常会通过可达性分析算法来判断对象是否存活

在确定了哪些垃圾可以被回收后

垃圾收集器(如 CMS、G1、ZGC)要做的事情就是进行垃圾回收

可以采用:

  • 标记清除算法
  • 复制算法
  • 标记整理算法
  • 分代收集算法等

C++ 等语言创建对象要不断的去开辟空间,不用的时候又需要不断的去释放空间,既要写构造函数,又要写析构函数

于是,有人就提出,能不能写一段程序实现这块功能,每次创建对象、释放内存空间的时候复用这段代码

1960 年,John McCarthy 在设计 Lisp 语言时首次提出了垃圾回收的概念,用于自动管理内存,避免程序员手动释放内存带来的错误


codejavajvm八股大约 6 分钟
JVM 八股9 - 垃圾收集2 (STW + 垃圾收集算法)

STW

"Stop The World"是 Java 垃圾收集中的一个重要概念。在垃圾收集过程中,JVM 会暂停所有的用户线程,这种暂停被称为"Stop The World"事件

这么做的主要原因是为了防止在垃圾收集过程中,用户线程修改了堆中的对象,导致垃圾收集器无法准确地收集垃圾

值得注意的是,"Stop The World"事件会对 Java 应用的性能产生影响

如果停顿时间过长,就会导致应用的响应时间变长,对于对实时性要求较高的应用,如交易系统、游戏服务器等,这种情况是不能接受的。

因此,在选择和调优垃圾收集器时,需要考虑其停顿时间。Java 中的一些垃圾收集器,如 G1 和 ZGC,都会尽可能地减少了"Stop The World"的时间,通过并发的垃圾收集,提高应用的响应性能。


codejavajvm八股大约 2 分钟
JVM 八股1 - 整体架构

JVM

学习 JVM 可以帮助我们开发者更好地优化程序性能、避免内存问题

  • 了解 JVM 的内存模型和垃圾回收机制,可以帮助我们更合理地配置内存、减少 GC 停顿
  • 掌握 JVM 的类加载机制可以帮助我们排查类加载冲突或异常
  • JVM 还提供了很多调试和监控工具,可以帮助我们分析内存和线程的使用情况,从而解决内存溢出内存泄露等问题

所以主要内容:内存模型、垃圾回收机制、类加载机制、JVM 调试/监控 工具

什么是 JVM


codejavajvm八股大约 7 分钟
JVM 八股2 - 内存管理1 (内存区介绍)

JVM 启动后,大致可以分为三个功能区:类加载器、运行时数据区以及执行引擎

内存管理

内存区其实是功能区中"运行时数据区"的具体细分

JVM(功能区视角)
│
├── 类加载器
│
├── 运行时数据区  ←──── 这部分就是"内存区"
│   ├── 程序计数器
│   ├── 虚拟机栈
│   ├── 本地方法栈
│   ├── 堆
│   └── 方法区
│
└── 执行引擎
    ├── JIT 编译器
    └── 垃圾回收器

codejavajvm八股大约 10 分钟
JVM 八股3 - 内存管理2 (native 介绍 | OOM 讨论)

JVM3

当 Java 应用需要与操作系统底层或硬件交互时,通常会用到本地方法栈。

native 方法

native 方法是在 Java 中通过 native 关键字声明的,用于调用非 Java 语言,如 C/C++ 编写的代码。Java 可以通过 JNI,也就是 Java Native Interface 与底层系统、硬件设备、或者本地库进行交互

Java 的 native 方法本身没有 Java 代码实现,而是交给本地代码实现

public native void test();

codejavajvm八股大约 8 分钟
JVM 八股4 - 内存管理3

虚拟机栈、本地方法栈、程序计数器 | 堆、方法区

不同 JDK 下内存区域变化

主要是方法区实现的不同

方法区是 JVM 规范里的概念, JVM 需要有这么一块区域来存类相关信息

HotSpot 实现层面:曾经用永久代、后来用元空间来实现

JDK 1.6 使用永久代来实现方法区:

alt text

JDK 1.7 时仍然是永久带,但发生了一些细微变化,比如将字符串常量池、静态变量存放到了堆上。


codejavajvm八股大约 11 分钟
OS5 - 进程管理3

OS

Java 线程状态

Java 没有 Ready 状态,线程状态有 6 种:

NEW              新建
RUNNABLE         可运行(包含 Ready + Running)
BLOCKED          阻塞(等待锁)
WAITING          等待(wait/join/park 等)
TIMED_WAITING    限时等待
TERMINATED       终止

codejavaCS大约 2 分钟