JVM与垃圾回收过程🔥

10/3/2024 JVM

# JVM中虚拟机组成是包括哪些部分?

JVM(Java虚拟机)主要由以下几个部分组成:

  1. 类加载子系统:负责加载类文件,校验、解析和初始化类数据。

  2. 运行时数据区:JVM运行时使用的内存区域,包含以下几个部分:

    • 方法区:存储已加载类的信息、常量、静态变量、即时编译器编译后的代码等。
    • :存放对象实例,是Java内存管理的主要区域。
    • 虚拟机栈:每个线程都有自己的虚拟机栈,里面存储局部变量、操作数栈和方法调用信息。
    • 本地方法栈:为使用Native方法服务的内存区。
    • 程序计数器:记录每个线程当前执行的字节码行号。
  3. 执行引擎:负责执行字节码指令,包括解释器、即时编译器(JIT)等。

  4. 本地方法接口:调用非Java代码(如C/C++编写的本地方法)的接口。

  5. 垃圾回收器(GC):自动回收不再使用的对象,管理堆内存。

这些部分共同构成了JVM的运行环境,负责Java程序的执行与内存管理。

# 堆是由哪几部分组成?

在JVM中,是用于存储所有对象实例的主要区域,通常划分为以下几个部分:

  1. 新生代(Young Generation):存放新创建的对象。新生代分为三部分:

    • Eden区:大多数新对象最先分配在这里,Eden区满了会触发一次Minor GC。
    • From Survivor区:新生代中幸存下来的对象从Eden区移到此处。
    • To Survivor区:每次Minor GC后,From Survivor区中的幸存对象被移到To Survivor区。下一次GC时,From和To区互换角色。
  2. 老年代(Old Generation):存放经过多次GC仍然存活的对象,通常是生命周期较长的对象。对象从新生代晋升到老年代。

  3. 元空间(Metaspace):在JDK 8之后,元数据(如类信息)不再存储在堆中的“方法区”,而是使用元空间,位于堆外的本地内存。
    (在JDK 8之前,元空间部分是方法区的一部分,称为“永久代”。)

  4. 大对象区(Large Object Area, LOA):一些JVM实现中,针对特别大的对象(如数组或大块数据)会有专门的空间,避免频繁的GC操作。

# 垃圾回收相关:

堆中的对象由垃圾回收器(GC)管理,通过不同的算法和策略(如标记-清除、标记-整理、复制等)回收不再使用的对象。

这种分代的设计是为了提高垃圾回收的效率,因为新生代的对象大多数会很快变得不可达,而老年代的对象生命周期较长。

# 一个对象完整的走完堆中的区域的过程是怎么样的?

一个对象在JVM堆中完整的生命周期通常会经历以下几个阶段,从对象创建开始,直到被垃圾回收。我们将通过对象在堆中的“新生代”和“老年代”移动过程,详细描述这一过程。

# 1. 对象创建

  • 当一个Java对象被创建时,首先分配在新生代(Young Generation)的Eden区
  • 如果Eden区没有足够的空间容纳新的对象,则会触发一次Minor GC(年轻代垃圾回收)

# 2. 第一次Minor GC

  • Minor GC会回收Eden区中那些不可达的对象(不再被任何引用所指向的对象)。
  • 经过GC后,仍然存活的对象将被移到From Survivor区
  • 此时Eden区中的空间被清空,准备接收后续的新对象。

# 3. Survivor区之间的交换

  • 当Eden区再次填满并且再次触发Minor GC时,Eden区和From Survivor区中的存活对象会被复制到To Survivor区(空的Survivor区),原本的From Survivor区会被清空。
  • 新生代中存在两个Survivor区(From和To区),它们会在每次GC后交换角色,即From Survivor和To Survivor轮流充当复制的源区和目标区。

# 4. 多次GC后的晋升到老年代

  • 对象每次在新生代的GC中存活下来,都会在Survivor区中“晋升”,即其**存活年龄(GC次数)**会增加。
  • JVM会维护一个阈值,通常称为晋升年龄阈值(Tenuring Threshold),当对象的存活年龄达到该阈值后,对象就会从Survivor区晋升到老年代(Old Generation)
  • 老年代用于存放生命周期较长的对象,因为这些对象存活的时间比新生代对象长得多,且不需要频繁的GC。

# 5. 老年代的对象回收(Major GC/Full GC)

  • 随着程序运行,老年代会逐渐填满。如果老年代的空间也不足以容纳更多对象,就会触发一次Major GCFull GC
  • Major GC是对老年代进行垃圾回收,通常比Minor GC要慢得多,因为它会遍历更多对象并且回收老年代的对象。
  • Full GC是对整个堆(包括新生代和老年代)进行的垃圾回收,通常是最后的手段,执行速度较慢,可能会导致应用程序的停顿。

# 6. 老年代中的对象被回收

  • 在老年代中,对象的生命周期相对较长,但它们最终也可能变得不可达。一旦老年代中的对象不再被引用(不可达),它们将被Major GC或Full GC回收。
  • 如果Major GC后老年代中没有足够的空间来存放新晋升的对象或者大对象,JVM可能会抛出OutOfMemoryError,表示堆空间不足。

# 7. 大对象的处理

  • 对于那些非常大的对象,JVM可能会直接将它们分配到老年代中,而不是先经过新生代(这个机制根据不同的JVM实现可能有所不同)。
  • 这样做的原因是为了避免在新生代频繁进行复制和GC时浪费资源。

# 8. 元空间的管理(在JDK 8及以后)

  • 类的元数据(如类的结构、方法信息等)在JDK 8之后不再存放在堆中,而是存放在**元空间(Metaspace)**中。元空间是JVM使用的本地内存区域,不在堆中。
  • 当元空间中的类不再使用时,也会被GC回收。

# 对象的完整生命周期总结:

  1. 对象最初创建时分配在新生代的Eden区。
  2. Eden区的空间满了后,触发Minor GC,存活的对象会被移动到From Survivor区。
  3. 多次GC后,存活时间较长的对象会在Survivor区中移动,直到达到晋升阈值,被移动到老年代。
  4. 老年代中的对象最终会在Major GC或Full GC中被回收,直到其生命周期结束。

这个过程通过分代收集的策略,最大化了GC的效率,尤其是对那些生命周期较短的对象,它们会在新生代频繁被回收,而那些生命周期较长的对象则被放到老年代进行较少的回收。

    我很快乐-周兴哲
    致逝去的青春