在 Java 程序执行过程中,Java 虚拟机将其在主内存中管理的内存划分为多个区域,每个区域存储不同类型的数据。内存空间中最大的一部分称为堆(Heap),它是 Java 虚拟机的运行时数据区,所有的实例和数组都存储在这里。
堆是在 Java 虚拟机启动时创建的,由所有线程共享,也是垃圾回收器的主要工作区域,因此这部分区域也称为堆内存或GC 堆。当垃圾回收器回收堆中的数据时,会扫描并清理通过 new
关键字创建的无用对象,释放内存,避免内存资源浪费。
为了提高垃圾回收的效率,堆内存进一步划分为年轻代、老年代和永久代。
年轻代:包括 Eden 区、SurvivorFrom 区和 SurvivorTo 区,用于存放新创建的对象。大多数新对象分配在 Eden 区(如果对象过大,则直接分配到老年代),年轻代的垃圾回收过程称为Minor GC。当 Eden 区内存不足时,会触发 Minor GC。
Minor GC 开始前,对象只存在于 Eden 区和 SurvivorFrom 区;Minor GC 过程中,Eden 区和 SurvivorFrom 区中存活的对象会被移动到 SurvivorTo 区,年龄加 1,同时 Eden 区和 SurvivorFrom 区被清空;Minor GC 结束后,SurvivorFrom 区和 SurvivorTo 区的角色互换,下一次 Minor GC 时,SurvivorTo 区和 Eden 区中存活的对象会被移动到 SurvivorFrom 区,并计算对象年龄。当对象年龄达到 15 时,会被分配到老年代。
老年代:也称为 Tenured 区,用于存放从年轻代存活下来的对象。老年代的垃圾回收过程称为Major GC。老年代存放更稳定的对象,不会频繁执行 Major GC。只有当新对象进入老年代导致空间不足,或者程序无法找到足够大的连续空间分配给新创建的大对象时,才会触发 Major GC。
由于需要扫描和回收,Major GC 所需时间较长。Major GC 会产生内存碎片,当老年代也没有足够内存分配给新对象时,会抛出 OOM(Out of Memory)异常。
永久代:也称为永久存储区,主要存储 Class 和 Meta(元数据)信息。在 Java 8 中,永久代被移除,取而代之的是 Metaspace 区。Metaspace 不在虚拟机内存中,而是使用本地内存,因此默认情况下,Metaspace 的大小仅受本地内存限制。
非堆内存指的是 Java 虚拟机堆外管理的内存区域,在 Java 虚拟机堆内存之外的内存区域分配一些对象实例,这些区域由操作系统直接管理(而非虚拟机管理),包括 Code Cache 区、Metaspace/永久代空间。
各区域说明如下表所示。
非堆内存区域 | 说明 |
---|---|
Code Cache | 用于编译和存储本地代码的区域。 |
Permanent Space | 存储虚拟机静态数据的区域,如类和方法对象。 |
Meta Space | Metaspace,存储类元数据的本地内存区域。 |
Direct Buffer | 直接缓冲区区域。 |