深入理解Java垃圾回收机制与类加载器

91 阅读4分钟

运行时常量池

运行时常量池是 JVM 在运行时动态生成的一块内存区域,用于存储编译期生成的各种字面量和符号引用。在 JDK 8 之前,运行时常量池位于方法区中,而在 JDK 8 及之后版本,它被移到了堆中。运行时常量池不仅包含 Class 文件中的常量池数据,还包括 JVM 运行期间生成的一些常量。

标记清除算法、标记整理算法和标记复制算法

  1. 标记清除算法: 首先标记所有需要回收的对象,然后对标记的对象进行清除。这种算法会产生不连续的内存碎片,影响内存的分配效率。
  2. 标记整理算法: 类似于标记清除算法,但在标记完成后会对存活对象进行整理,使它们在内存中变得连续。这样可以避免内存碎片问题。
  3. 标记复制算法: 将内存空间划分为两个区域,一部分用于存储对象,另一部分保持空闲。当对象存活时,将其复制到空闲区域,然后清理已使用区域。这样可以避免内存碎片,但是需要额外的空间用于复制对象。

分代收集算法

分代收集算法根据对象的存活周期将堆内存划分为不同的区域,一般分为新生代和老年代。新生代使用复制算法,老年代使用标记整理或标记清除算法。这样可以根据不同区域的特点采用更适合的垃圾回收算法,提高效率。

Serial、ParNew、Parallel Scavenge、CMS、G1 收集器简述

  1. Serial 收集器: 单线程,新生代使用标记复制算法,老年代使用标记整理算法,适用于单核 CPU 环境。
  2. ParNew 收集器: 多线程版本的 Serial 收集器,新生代使用标记复制算法,老年代使用标记整理算法,适用于多核 CPU 环境。
  3. Parallel Scavenge 收集器: 多线程,注重吞吐量,适用于后台运算等对系统资源要求不敏感的场景。
  4. CMS(Concurrent Mark-Sweep)收集器: 以最短的停顿时间为目标,采用并发标记和并发清除算法,适用于对系统响应时间有要求的场景。
  5. G1(Garbage First)收集器: 将堆内存分为多个大小相等的区域,具有高灵活性,可通过设置参数调整吞吐量和停顿时间,适用于大内存应用和对低延迟有要求的场景。

Minor GC 和 Full GC

  • Minor GC: 发生在新生代的垃圾收集,主要回收新生代,频繁且速度较快。
  • Full GC: 清理整个堆,包括新生代、老年代和永久代(或元空间)。发生 Full GC 通常伴随着较长的停顿时间,对系统影响较大。

内存分配策略

  • 大多数对象在新生代 Eden 区分配,当 Eden 满时触发 Minor GC。
  • 大对象直接进入老年代。
  • 对象优先在 Eden 区分配,如果经过一次 Minor GC 后仍然存活且能容纳,则进入 Survivor 区;否则直接进入老年代。
  • 长期存活的对象将进入老年代。

JVM 类加载过程

  1. 加载(Loading): 通过类的全限定名获取类的二进制字节流。
  2. 验证(Verification): 对字节流进行各种验证,确保符合 JVM 规范。
  3. 准备(Preparation): 为类变量分配内存并设置默认初始值。
  4. 解析(Resolution): 将符号引用转化为直接引用。
  5. 初始化(Initialization): 执行类构造器 <clinit> 方法,初始化类变量和静态代码块。

类加载器和双亲委派机制

  • BootstrapClassLoader: 加载 Java 核心类库,由 C++ 编写。
  • ExtensionClassLoader: 加载扩展类库,由 Java 编写。
  • AppClassLoader: 加载应用程序类,由 Java 编写。

双亲委派机制:一个类加载器在加载类时,先将加载请求委派给父类加载器,一直委派到启动类加载器,只有在父类加载器无法加载时才尝试自己加载。这样确保类加载的一致性和避免类的重复加载。

破坏双亲委派机制

破坏双亲委派机制的方式主要是重写 loadClass 方法,不调用父类加载器,直接自己加载类。这样会导致类的重复加载和破坏类加载的一致性。