Java中的内存与垃圾回收器:引擎盖下究竟发生了什么

面向初学者的在线Python训练器

轻松学习Python,无需理论负担。在浏览器中直接解决带自动检查的实践任务,无需安装任何东西。

开始课程

Java如何管理内存:不仅仅是“垃圾回收”

\n\n

许多从C++或Python转向Java的开发者起初会很高兴:“太好了,不用再担心内存泄漏了!”。然后他们在最不合时宜的时刻遇到了OutOfMemoryError,才明白——Java中的内存管理需要深入理解。今天,我们将深入JVM引擎盖下,剖析Java中的内存与垃圾回收器实际上是如何工作的。

\n\n

这篇文章既适合想要了解Java内存模型的新手,也适合正在寻找优化Java垃圾回收方法的有经验的开发者。我们开始吧!

\n\n
\n\n

Java内存是如何组织的:堆、栈和元空间

\n\n

在讨论Java垃圾回收器之前,需要理解你的对象究竟“住”在哪里。JVM将内存划分为几个关键区域:

\n\n
    \n
  • 堆 — 这里存储所有创建的对象。这是Java垃圾回收的主要工作区域。
  • \n
  • 栈 — 存储基本类型和指向堆中对象的引用。每个线程都有自己的栈。
  • \n
  • 元空间 — 在Java 8中取代了永久代,存储类的元数据。
  • \n
  • 代码缓存 — JIT编译器编译后的代码。
  • \n
\n\n

下面是一个简单的例子,展示了数据存储的位置:

\n\n
public class MemoryDemo {\n    public static void main(String[] args) {\n        int number = 42;                    // 基本类型 — 在栈中\n        String text = "Hello";              // 引用在栈中,对象在堆中\n        List<String> list = new ArrayList<>(); // 引用在栈中,列表在堆中\n        list.add("Java");                   // 字符串"Java" — 也在堆中\n    }\n}\n
\n\n

关键点:所有通过new创建的对象都进入Java堆。正是这个区域由Java中的垃圾回收器“监控”。

\n\n
\n\n

为什么垃圾回收器不是敌人,而是朋友(如果你理解它的逻辑)

\n\n

许多开发者因为“Stop-The-World”暂停而害怕Java GC。但让我们面对现实:没有自动的Java内存管理,我们就得手动编写free()并遭遇段错误。Java垃圾回收器不是一个邪恶的恶魔,而是一个复杂、智能的机制。

\n\n

Java垃圾回收工作的基本原则:

\n\n
    \n
  • 如果一个对象没有来自栈或静态字段的活动引用,它就被认为是“垃圾”。
  • \n
  • GC不能删除仍然有引用的对象(即使你忘记了它)。
  • \n
  • 不同的GC算法有不同的目标:最小化暂停、最大化吞吐量或节省内存。
  • \n
\n\n

下面是一个经典的内存泄漏示例,对象被“遗忘”在集合中:

\n\n
public class LeakExample {\n    private static List<byte[]> cache = new ArrayList<>();\n\n    public static void addToCache() {\n        byte[] data = new byte[1024 * 1024]; // 1 MB\n        cache.add(data); // 引用仍然存在 — GC不会碰它\n    }\n\n    public static void main(String[] args) {\n        for (int i = 0; i < 1000; i++) {\n            addToCache();\n        }\n        // 这里会出现OutOfMemoryError,尽管对象已经不再需要\n    }\n}\n
\n\n

结论:只要你持有引用,Java中的内存就不会被释放。理解这一点是编写高效代码的第一步。

\n\n
\n\n

Java中的垃圾回收器类型:从Serial到ZGC

\n\n

现代Java提供了几种Java垃圾回收的实现。选择取决于你的任务:

\n\n

1. Serial GC — 适用于小型应用

\n

单线程收集器,会“冻结”整个应用程序。用于

博客

书籍推荐