JVM 内存深入理解

目录

堆内存

在 Java 程序执行过程中,Java 虚拟机将其在主内存中管理的内存划分为多个区域,每个区域存储不同类型的数据。其中占用内存空间最大的部分称为堆(Heap),它是 Java 虚拟机的运行时数据区,用于存放所有的实例和数组。

堆在 Java 虚拟机启动时创建,由所有线程共享,也是垃圾回收器的主要工作区域,因此这部分区域也称为堆内存GC 堆。当垃圾回收器回收堆中的数据时,会扫描并清理用 new 关键字创建的无用对象,释放内存,避免内存资源浪费。

为了提高垃圾回收效率,堆内存进一步划分为年轻代、老年代和永久代。

JVM Heap

  • 年轻代:包括 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 和元数据信息。在 Java 8 中,永久代被移除,取而代之的是 Metaspace 区。Metaspace 不在虚拟机内存中,而是使用本地内存,因此默认情况下,Metaspace 的大小仅受本地内存限制。

非堆内存

非堆内存指的是 Java 虚拟机堆外管理的内存区域,在 Java 虚拟机堆内存之外的内存区域分配的一些对象实例,这些区域由操作系统直接管理(非虚拟机管理),包括代码缓存区、Metaspace/永久代空间。

各区域说明如下表所示。

非堆内存区域说明
Code Cache用于编译和存储本地代码的区域。
Permanent Space用于存储虚拟机的静态数据,如类对象和方法对象的区域。
Meta SpaceMetaspace,存储类元数据的本地内存区域。
Direct Buffer直接缓冲区区域。