Skip to content

第八章:内存管理

8.1 JVM 运行机制

Java 程序的执行依赖于 Java 虚拟机(JVM)。JVM 负责将字节码转换成可以在操作系统上运行的机器代码,并提供一些重要的功能,包括内存管理、垃圾回收等。

8.1.1 ClassLoader

ClassLoader 是 JVM 的一部分,负责加载类文件。在 JVM 启动时,ClassLoader 会从文件系统或网络中加载类。它遵循一套父子加载机制,在加载时会按照指定的加载顺序搜索类。

ClassLoader 有几个常用类型:

  • Bootstrap ClassLoader:加载 JDK 核心库。

  • Extension ClassLoader:加载 JDK 扩展目录下的类。

  • Application ClassLoader:加载应用程序的类路径下的类。

8.1.2 运行时数据区

JVM 内部的内存被划分为几个区域,每个区域有特定的功能。主要的内存区域包括:

  • 方法区:存储类的元数据(如类的信息、方法、常量池等)。

  • 堆:存储对象实例,是垃圾回收的主要区域。堆可以被细分为年轻代(Young Generation)和老年代(Old Generation)。

  • 栈:存储局部变量和方法调用栈帧,每个线程都会有自己的栈。

  • 程序计数器:指示当前线程所执行的字节码指令的地址。

  • 本地方法栈:用于支持 native 方法的执行。

8.2 垃圾回收(GC)机制

Java 中的垃圾回收(GC)机制负责自动管理内存的回收,主要通过回收不再使用的对象来释放内存空间。GC 的目标是避免内存泄漏并优化内存的使用。

8.2.1 垃圾回收过程

  • 标记(Mark):识别出哪些对象是可达的。

  • 清除(Sweep):删除不可达的对象,回收内存。

  • 压缩(Compact):压缩内存,防止内存碎片。

8.2.2 垃圾回收的几种算法

  • 复制算法:将内存划分为两个区域,活动区域与空闲区域,每次回收时将存活对象复制到空闲区域。

  • 标记-清除算法:首先标记所有存活对象,然后清除未标记的对象。

  • 标记-整理算法:标记存活对象后,通过整理内存区域来消除碎片。

  • 分代回收算法:将堆分为年轻代、老年代和永久代,不同代采用不同的回收策略。

8.2.3 垃圾回收器

Java 提供了多种垃圾回收器,常见的包括:

  • Serial GC:单线程垃圾回收器,适用于内存较小的单核机器。

  • Parallel GC:多线程垃圾回收器,适用于多核机器。

  • CMS GC:低延迟垃圾回收器,适用于对响应时间要求较高的应用。

  • G1 GC:分代收集垃圾回收器,适用于大内存应用,能够更好地控制暂停时间。

8.3 强引用、软引用、弱引用、虚引用

在 Java 中,引用对象有多种类型,这些引用类型决定了对象的回收时机。

8.3.1 强引用(Strong Reference)

最常见的引用类型,即普通的对象引用。如果一个对象有强引用,则垃圾回收器不会回收该对象。

java
Object obj = new Object();  // obj 是强引用

8.3.2 软引用(Soft Reference)

软引用是一种可以在系统内存不足时被垃圾回收器回收的引用类型。软引用通常用于缓存,保证对象在内存充足时可以保留,在内存不足时回收。

java
SoftReference<MyObject> softRef = new SoftReference<>(new MyObject());

8.3.3 弱引用(Weak Reference)

弱引用比软引用更弱,只有弱引用的对象在垃圾回收时会被回收。即使系统内存没有不足,垃圾回收器也会回收弱引用指向的对象。

java
WeakReference<MyObject> weakRef = new WeakReference<>(new MyObject());

8.3.4 虚引用(Phantom Reference)

虚引用是最弱的引用类型,虚引用不能通过其引用对象来访问,主要用于跟踪垃圾回收器的回收过程。当垃圾回收器准备回收一个对象时,虚引用会被通知。

java
PhantomReference<MyObject> phantomRef = new PhantomReference<>(new MyObject(), referenceQueue);

8.4 内存泄漏与优化

8.4.1 内存泄漏的定义

内存泄漏是指程序中有些对象不再使用,但由于某些原因(如不当的引用保持),这些对象无法被垃圾回收器回收,导致内存占用不断增加,最终可能导致系统崩溃。

8.4.2 内存泄漏的常见原因

  • 静态集合类:静态集合类常常因为全局存在而无法回收对象。

  • Listener 和 Callback:未能移除注册的监听器或回调函数。

  • ThreadLocal:线程局部变量未被清理。

  • 数据库连接池和文件句柄:未关闭的连接或资源未释放。

8.4.3 内存泄漏的优化

  • 定期清理无用对象:例如在缓存使用完后清理不再需要的缓存对象。

  • 手动管理资源:对于数据库连接、IO 流等资源,确保在使用完后关闭。

  • 使用 WeakReference:对于某些缓存对象,可以使用软引用或弱引用,允许垃圾回收器在需要时回收它们。

  • 代码审查和工具检测:使用内存分析工具(如 jmap、jvisualvm)进行内存泄漏检测。

总结

  • JVM 运行机制:JVM 负责类的加载和内存的管理,堆内存和栈内存是两种关键内存区域,垃圾回收机制优化了内存使用。

  • 垃圾回收(GC):通过标记、清理和压缩等步骤回收内存,Java 提供了多种回收算法和回收器。

  • 引用类型:Java 提供了强引用、软引用、弱引用和虚引用,控制对象的回收时机。

  • 内存泄漏与优化:内存泄漏常因不当引用、资源未释放等引起,应通过清理无用对象、使用弱引用、手动管理资源等手段优化内存使用。

内存管理是 Java 编程中的重要课题,理解 JVM 的工作机制、垃圾回收机制和内存管理的细节,能帮助开发者编写更加高效、健壮的应用程序。