jvm 知识汇总

JVM 管理

堆内存 (Heap Memory)

  • 是什么:它是 JVM 运行时管理的一块最大的内存区域,是所有线程共享的。

  • 做什么:几乎所有通过 new 关键字创建的对象和数组都在这里分配内存。你可以把它想象成一个巨大的“对象公寓楼”,所有新创建的对象都住在这里。垃圾回收器(GC)的主要工作场所就是这片区域。

👶 年轻代 (Young Generation)

  • 是什么:堆内存的一部分,通常占堆总大小的 1/3。

  • 做什么:这是对象的“出生地”和“托儿所”。绝大多数新创建的对象都会首先被分配到年轻代。它的特点是对象数量多,但生命周期普遍很短,很多对象“朝生夕死”。

  • 内部结构:年轻代内部又细分为一个 Eden 区和两个 Survivor 区。大部分对象在 Eden 区诞生,经过筛选后,存活的会进入 Survivor 区。

👴 老年代 (Old Generation)

  • 是什么:堆内存的另一部分,通常占堆总大小的 2/3。
  • 做什么:这里存放的是“长寿命”的对象。当一个对象在年轻代里经历了几次“考核”(GC)后依然存活,就会被晋升到老年代,相当于从“托儿所”毕业,进入了“养老院”。一些体积特别大的对象也可能直接分配到老年代。

🧹 Young GC (YGC / Minor GC)

  • 是什么:专门针对年轻代的垃圾回收行为。

  • 触发时机:当年轻代的 Eden 区空间不足时,就会触发一次 Young GC。

  • 过程与特点
    1. 它会快速扫描年轻代,将那些不再被引用的“短命”对象清理掉。
    2. 幸存下来的对象年龄会+1,并被移动到另一个 Survivor 区。
    3. 如果对象的年龄达到了预设的阈值(默认是15),它就会从年轻代晋升到老年代
    4. Young GC 发生非常频繁,但因为只清理年轻代,且大多数对象都是垃圾,所以速度很快,停顿时间也相对较短。

🚨 Full GC (FGC / Major GC)

  • 是什么:对整个堆内存(包括年轻代老年代以及元空间等)进行的一次全面、彻底的垃圾回收。
  • 触发时机:通常在以下几种情况发生:
    • 老年代空间不足,无法容纳从年轻代晋升上来的对象。
    • 显式调用了 System.gc() 方法(但不保证一定会执行)。
    • 元空间(Metaspace)空间不足。
  • 过程与特点
    • Full GC 是一次“大扫除”,它会整理整个堆的内存,因此耗时非常长,会导致应用程序出现明显的卡顿(Stop-The-World)。
    • 在性能调优中,我们的核心目标之一就是尽量避免或减少 Full GC 的发生频率

垃圾回收器(Garbage Collector, GC)

  Java 虚拟机(JVM)实现自动内存管理的核心组件。它的主要职责是自动识别并回收程序中不再使用的对象所占用的内存,从而避免内存泄漏,减轻开发者的负担。
GC 的设计始终围绕着一个“不可能三角”进行权衡:低延迟(减少程序停顿)、高吞吐量(最大化程序运行时间)和小内存占用(高效利用内存)。没有一种 GC 能在所有方面都做到最优,因此 JVM 提供了多种收集器以适应不同的应用场景。

  

常用垃圾回收器

  每个版本的 jdk 对应的垃圾回收器是不同的,回收策略方式也不同,适配不同场景

  CMS 回收器

  -XX:+UseConcMarkSweepGC

  gc 日志如下

2026-04-22T18:29:11.992+0800: 634.333: [GC (Allocation Failure) 634.333: [ParNew: 1855206K->150153K(1887488K), 0.1371639 secs] 3921700K->2256434K(6081792K), 0.1373426 secs] [Times: user=0.
49 sys=0.01, real=0.14 secs] 
2026-04-22T18:29:12.130+0800: 634.471: [GC (CMS Initial Mark) [1 CMS-initial-mark: 2106281K(4194304K)] 2260231K(6081792K), 0.0390175 secs] [Times: user=0.13 sys=0.01, real=0.04 secs] 
2026-04-22T18:29:12.170+0800: 634.511: [CMS-concurrent-mark-start]
2026-04-22T18:29:12.481+0800: 634.822: [CMS-concurrent-mark: 0.311/0.311 secs] [Times: user=0.61 sys=0.02, real=0.31 secs] 
2026-04-22T18:29:12.481+0800: 634.822: [CMS-concurrent-preclean-start]
2026-04-22T18:29:12.829+0800: 635.170: [CMS-concurrent-preclean: 0.347/0.348 secs] [Times: user=0.64 sys=0.06, real=0.35 secs] 
2026-04-22T18:29:12.829+0800: 635.170: [CMS-concurrent-abortable-preclean-start]
2026-04-22T18:29:16.758+0800: 639.099: [GC (Allocation Failure) 639.099: [ParNew: 1827977K->209664K(1887488K), 0.1274023 secs] 3934258K->2316166K(6081792K), 0.1275787 secs] [Times: user=0.49 sys=0.00, real=0.13 secs] 
 CMS: abort preclean due to time 2026-04-22T18:29:20.086+0800: 642.427: [CMS-concurrent-abortable-preclean: 6.932/7.257 secs] [Times: user=27.16 sys=1.21, real=7.25 secs] 
2026-04-22T18:29:20.086+0800: 642.427: [GC (CMS Final Remark) [YG occupancy: 1104159 K (1887488 K)]642.427: [Rescan (parallel) , 0.2847491 secs]642.712: [weak refs processing, 0.0026899 secs]642.715: [class unloading, 0.0479635 secs]642.763: [scrub symbol table, 0.0227686 secs]642.786: [scrub string table, 0.0017714 secs][1 CMS-remark: 2106502K(4194304K)] 3210661K(6081792K), 0.3676621 secs] [Times: user=1.21 sys=0.00, real=0.37 secs] 
2026-04-22T18:29:20.455+0800: 642.796: [CMS-concurrent-sweep-start]
2026-04-22T18:29:22.722+0800: 645.063: [GC (Allocation Failure) 645.063: [ParNew: 1887488K->160588K(1887488K), 0.2092911 secs] 3498855K->1879131K(6081792K), 0.2094598 secs] [Times: user=0.76 sys=0.00, real=0.21 secs] 
2026-04-22T18:29:27.316+0800: 649.657: [GC (Allocation Failure) 649.657: [ParNew: 1838412K->108201K(1887488K), 0.0765652 secs] 3228881K->1498670K(6081792K), 0.0767189 secs] [Times: user=0.30 sys=0.00, real=0.08 secs] 
2026-04-22T18:29:32.321+0800: 654.662: [GC (Allocation Failure) 654.662: [ParNew: 1786025K->146810K(1887488K), 0.0888121 secs] 2609420K->970205K(6081792K), 0.0889518 secs] [Times: user=0.35 sys=0.01, real=0.09 secs] 
2026-04-22T18:29:38.005+0800: 660.346: [GC (Allocation Failure) 660.346: [ParNew: 1824634K->209664K(1887488K), 0.1503263 secs] 2493190K->914569K(6081792K), 0.1504979 secs] [Times: user=0.56 sys=0.01, real=0.15 secs] 
2026-04-22T18:29:38.563+0800: 660.904: [CMS-concurrent-sweep: 17.054/18.107 secs] [Times: user=66.64 sys=2.87, real=18.11 secs] 
2026-04-22T18:29:38.563+0800: 660.904: [CMS-concurrent-reset-start]
2026-04-22T18:29:38.671+0800: 661.012: [CMS-concurrent-reset: 0.108/0.108 secs] [Times: user=0.26 sys=0.13, real=0.11 secs] 
2026-04-22T18:29:41.671+0800: 664.012: [GC (Allocation Failure) 664.012: [ParNew: 1887488K->172335K(1887488K), 0.1576992 secs] 2527824K->875967K(6081792K), 0.1578523 secs] [Times: user=0.59 sys=0.00, real=0.16 secs] 

View Code

   触发条件:

    老年代使用率过高,空间不足

   过程:

      1、CMS-concurrent-mark(并发标记):后台与业务线程并行完成,初步标记

        2、CMS-concurrent-preclean+CMS-concurrent-abortable-preclean(并发预清理):它继续做和预清理一样的工作(扫描脏卡、处理新生代引用),为了减轻下一个阶段——也就是那个会暂停用户的 “重新标记 (Remark)” 阶段的压力

      3、CMS Final Remark(最终标记):触发触发 STW (Stop-The-World),暂停所有应用线程,以确保在这一瞬间对象引用关系不再变化。然后,GC 会快速修正那些在并发期间发  生变动的对象的标记记录,保证最终结果的准确性。

      4、清理(CMS-concurrent-sweep):真正执行清理垃圾操作

        5、重置(CMS-concurrent-reset):结束清理,重置数据和状态为下次做准备

       总结:

    设计原则是最小化 STW,提前标记,牺牲部分 CPU 开销(后台线程提前标记扫描,与业务线程并行)

    在容器环境配置了 limit后,由于 cpu限制 CFS 机制会导致 cpu使用上安装毫秒分时分片容易导致标记线程抢不到 cpu资源,而标记过程一般需要持续 2-3 秒,不允许中断,否则回收失败,最终触发 Full GC,长时间 STW 进而导致业务线程被挂起无响应

 

  G1(Garbage-First)

   自 JDK 9 起成为 HotSpot 虚拟机的默认选择。它的设计目标是在大内存、多核处理器的环境下,实现高吞吐量与可预测的低延迟之间的最佳平衡。
   -XX:+UseG1GC  

    G1 的核心创新在于其独特的内存布局和回收策略,旨在克服传统分代收集器的局限性。
   Region 化的内存布局
    G1 摒弃了新生代和老年代在物理上完全隔离的布局,而是将整个 Java 堆划分为多个大小相等的独立区域(Region),每个 Region 的大小在 1MB 到 32MB 之间。
逻辑分代:虽然物理上是独立的 Region,但 G1 仍然保留了分代的概念。每个 Region 在不同时刻可以扮演不同的角色:Eden、Survivor、Old 或 Humongous(专门存放超过 Region 大小 50% 的大对象)。
.     

      工作流程与关键阶段    

    G1 的垃圾回收主要分为两种模式:Young GC 和 Mixed GC。
    Young GC (年轻代回收)
      当 Eden 区的 Region 被耗尽时触发。它是一个完全的 STW(Stop-The-World)操作,采用复制算法,将 Eden 和 Survivor 区中的存活对象复制到新的 Survivor Region 或直接晋升到老年代。
    Mixed GC (混合回收,这是 G1 的核心)
      当老年代的占用率达到特定阈值(由 -XX:InitiatingHeapOccupancyPercent 控制,默认为 45%)时,会触发一个并发的标记周期,随后执行一系列的 Mixed GC。

      Mixed GC 不仅回收所有的年轻代 Region,还会额外选择一部分老年代 Region 进行回收。
      这个并发标记周期包含以下几个关键阶段:
      初始标记 (Initial Mark):STW 阶段。标记从 GC Roots 直接可达的对象。此阶段通常会与一次 Young GC 同步进行,以复用其停顿时间。
      并发标记 (Concurrent Mark):与应用线程并发执行。遍历整个对象图,找出所有存活的对象。此阶段使用 SATB (Snapshot-At-The-Beginning) 算法来保证标记的准确性。
      最终标记 (Final Mark / Remark):STW 阶段。处理在并发标记期间因应用运行而产生的少量引用变动,完成最终的标记工作。
      筛选回收 (Live Data Counting & Evacuation):STW 阶段。根据预设的停顿时间目标,选择一批“价值”最高的老年代 Region,将其中的存活对象复制到其他空闲 Region 中,然后清空  这些被回收的 Region。

    Full GC

      当 Mixed GC也无法回收内存到理想水位就会触发 Full GC  

   总结    

     优势
      平衡性能:在吞吐量和延迟之间取得了很好的平衡,既不像 Parallel GC 那样停顿时间长,也不像 CMS 那样牺牲吞吐量。
      无内存碎片:通过在回收过程中复制和整理存活对象,G1 能有效避免内存碎片问题,这对于长期运行的应用至关重要。
      适合大堆:Region 化的设计使其能够高效地管理非常大的堆内存(如 6GB 以上)。

      可预测的停顿时间模型这是 G1 的一大亮点。它允许用户通过参数 -XX:MaxGCPauseMillis 明确指定期望的 GC 最大停顿时间(默认为 200ms)

      相比于 CMS 它的标记过程在容器环境不受 CMS 策略影响,被中断后可以继续,不会被终止,更适合容器环境
      
    劣势
      小堆场景不占优:在小堆内存(如小于 4GB)的场景下,其复杂的内部管理开销可能导致性能不如更简单的 Parallel GC。

 

JVM 启动参数

-Dfile.encoding=UTF-8
#高版本支持,容器里面自动识别 limit,根据 limit总量的 90% 作为堆内存是使用率,当然如果手动指定了 -Xms -Xmx 它俩优先级就更高了
-XX:+UseContainerSupport  
-XX:MaxRAMPercentage=90.0
#设置年轻代和老年代比例为 1:2,-Xmn优先级高于它 
-XX:NewRatio=2

#实时写 gc日志,打印详情,设置 gc日志路径
-XX:+PrintGCDetails
-XX:+PrintGCDateStamps
-verbose:gc
-Xloggc:/debug/gc.log
#发生oom时候退出之前导出堆内存快照,方便时候排查内存泄露原因
-XX:+HeapDumpOnOutOfMemoryError
-XX:HeapDumpPath=/debug/

#设置堆内存大小和元数据区大小 -Xms6g -Xmx8g -Xmn3g
-XX:MetaspaceSize=512M
-XX:MaxMetaspaceSize=512M

#配置GC回收器类型,以及GC时候STW时间
-XX:+UseConcMarkSweepGC
-XX:MaxGCPauseMillis=200
-XX:ConcGCThreads=1
 

 

posted @ 2026-04-28 19:00  fanggege  阅读(36)  评论(0)    收藏  举报