JVM基础50道经典面试题(一)

JVM基础50道经典面试题(一)

      • [💡 内存区域与模型(14题)](#💡 内存区域与模型(14题))
      • [💡 垃圾回收机制(20题)](#💡 垃圾回收机制(20题))
      • [💡 类加载机制(8题)](#💡 类加载机制(8题))
      • [💡 执行引擎与性能调优(8题)](#💡 执行引擎与性能调优(8题))
      • [💎 从理论到实践的建议](#💎 从理论到实践的建议)

围绕JVM学习框架,构建了50道高质量的面试题及深度分析,旨在帮助从"记忆"走向"理解",直至"调优"。

为了方便系统性地掌握和复习,将这50道题目整理到了下面的表格中,它们覆盖了JVM的四大核心模块:

类别 考察要点 关键题目编号
内存区域与模型 运行时数据区划分、核心原理、常见异常与配置 1 - 14
垃圾回收机制 对象回收判定、GC算法、各类收集器原理与调优实战 15 - 34
类加载机制 类加载过程、双亲委派模型及其破坏、应用 35 - 42
执行与性能调优 JIT编译器、性能监控工具、综合调优思路 43 - 50

💡 内存区域与模型(14题)

这部分是理解JVM的基石,面试官会通过它判断对程序运行底层环境的掌握程度。

  1. 请详细描述JVM的运行时数据区(Runtime Data Areas)。哪些是线程共享的,哪些是线程私有的?

    • 核心分析 :必须准确区分。线程共享 :堆(Heap)、方法区(Method Area,JDK8后为元空间 Metaspace)。线程私有:程序计数器(PC Register)、Java虚拟机栈(VM Stack)、本地方法栈(Native Method Stack)。需能阐述每个区域的核心作用。
  2. 程序计数器(Program Counter Register)的作用是什么?为什么它是线程私有的?

    • 核心分析 :① 作用:指向当前线程正在执行的字节码指令地址(或native方法的undefined)。② 线程私有原因:Java多线程通过CPU时间片轮转实现,一个CPU核心在任意时刻只能执行一个线程的指令。线程切换后需能恢复到正确位置,因此必须每个线程独立存储自己的执行地址。
  3. Java虚拟机栈(VM Stack)中存储的是什么?什么是栈帧(Stack Frame)?

    • 核心分析 :虚拟机栈存储栈帧。栈帧 是用于支持方法调用和执行的数据结构,每个方法从调用到完成对应一个栈帧的入栈和出栈。栈帧内部包含局部变量表 (基本类型和对象引用)、操作数栈动态链接方法返回地址等信息。
  4. 什么情况下会抛出 StackOverflowErrorOutOfMemoryError(在栈方面)?

    • 核心分析StackOverflowError:线程请求的栈深度超过 虚拟机栈所允许的最大深度(如无限递归)。OutOfMemoryError:虚拟机栈动态扩展时无法申请到足够内存(如创建过多线程,每个线程都需要独立的栈空间)。两者触发条件不同。
  5. 堆(Heap)为什么要分代(年轻代、老年代)?永久代(PermGen)和元空间(Metaspace)有什么区别?

    • 核心分析分代 基于"弱分代假说",让不同生命周期的对象处于不同区域,从而采用最合适的GC算法(如年轻代用复制算法)。永久代 vs 元空间:永久代在堆内,大小固定易OOM;元空间使用本地内存,默认无上限,且将字符串常量池等移入堆中,降低了OOM风险。
  6. 方法区(Method Area)和运行时常量池(Runtime Constant Pool)的关系是什么?

    • 核心分析 :方法区用于存储已被加载的类信息、常量、静态变量、即时编译器编译后的代码 等数据。运行时常量池是方法区的一部分,用于存放编译期生成的各种字面量和符号引用(在类加载后进入)。JDK8后,运行时常量池移到了堆中。
  7. 直接内存(Direct Memory)是什么?它和堆内存有什么区别?使用 ByteBuffer.allocateDirect() 有什么优缺点?

    • 核心分析 :直接内存不是JVM运行时数据区的一部分,而是通过Native函数库在堆外 直接分配的内存。区别 :不受JVM堆大小限制,但受本机总内存限制;避免了Java堆和Native堆间的数据复制(零拷贝),提升性能(如NIO)。优点 :性能高。缺点 :分配回收成本高,管理不当易导致OutOfMemoryError
  8. String 常量池在 JDK 1.7 和 JDK 1.8 中分别位于哪里?String.intern() 方法的行为是怎样的?

    • 核心分析 :JDK 1.7起,字符串常量池从方法区移到了堆中intern()是一个本地方法:如果池中已包含等于此String对象的字符串,则返回池中字符串的引用;否则,将此String对象包含的字符串添加到常量池中,并返回此String对象的引用(JDK7+会复制引用到池,而非复制整个字符串对象)。
  9. 对象在堆内存中的布局是怎样的?

    • 核心分析 :分为三块:对象头 (包含Mark Word和类型指针)、实例数据 (对象真正存储的有效信息)、对齐填充(非必需,仅起占位作用,保证对象大小是8字节的整数倍)。了解Mark Word对于理解锁升级至关重要。
  10. 一个对象从创建到被回收,在内存中是如何流转的?(结合新生代Eden, Survivor区说明)

    • 核心分析 :对象优先在Eden区 分配。Eden满后触发Minor GC,存活对象移到Survivor0 ("To"区)。再次Minor GC, Eden和Survivor0存活对象复制到Survivor1 ("From"区),并交换From/To指针。对象每熬过一次Minor GC,年龄+1。达到阈值(默认15)后,晋升到老年代。大对象可能直接进入老年代。
  11. 如何判断一个对象是否是"垃圾",可以被回收?引用计数法和可达性分析算法各有什么优缺点?

    • 核心分析引用计数法 :简单,但无法解决循环引用 问题。可达性分析(Java采用):从一系列"GC Roots"对象出发,向下搜索,不可达的对象即为垃圾。GC Roots包括:虚拟机栈中引用的对象、方法区静态属性引用的对象、方法区常量引用的对象、本地方法栈JNI引用的对象等。
  12. Java中的四种引用类型(强、软、弱、虚)对垃圾回收有什么影响?分别有什么使用场景?

    • 核心分析 :这是内存敏感应用的关键。强引用 :永不回收。软引用 :内存不足时回收,适合缓存。弱引用 :下次GC即回收,适合WeakHashMap等。虚引用 :无法通过它获取对象,用于追踪对象被GC的活动(与ReferenceQueue配合)。这体现了JVM内存管理的精细度。
  13. 什么是Stop-The-World(STW)?为什么GC过程中会发生STW?

    • 核心分析 :STW指在GC过程中,整个Java应用程序线程会被暂停 ,就像世界停止一样。原因:为了保证可达性分析的一致性GC期间对象引用关系不会发生变化,就像拍照时需要瞬间静止。所有GC器都有STW,只是暂停时间和频率不同,低延迟GC器(如ZGC)的核心目标就是尽可能缩短STW时间。
  14. 逃逸分析(Escape Analysis)是什么?JVM基于它可以做什么优化?

    • 核心分析 :分析对象动态作用域,判断对象是否会"逃逸"出方法或线程。基于此,JIT编译器可以进行:栈上分配 (对象在栈上创建销毁,减轻GC压力)、锁消除 (对线程局部对象移除同步锁)、标量替换 (将对象分解为基本类型在寄存器/栈上分配)。这些是重要的即时编译优化

💡 垃圾回收机制(20题)

这部分是JVM面试的核心和难点,需要深入理解算法、收集器及其调优。

  1. 常见的垃圾收集算法有哪些?请对比标记-清除(Mark-Sweep)、复制(Copying)、标记-整理(Mark-Compact)算法的优缺点。

    • 核心分析标记-清除 :产生内存碎片。复制 :无碎片,但浪费一半空间,适合对象存活率低的新生代。标记-整理:无碎片,但移动对象成本高,适合对象存活率高的老年代。这是选择收集器的基础。
  2. 什么是"卡表"(Card Table)?它如何优化跨代引用时的垃圾回收效率?

    • 核心分析 :一种解决跨代引用 (老年代对象引用新生代对象)问题的数据结构。将老年代划分为大小为512字节的"卡",维护一个卡表(字节数组)。当老年代对象引用新生代对象时,JVM将对应卡标记为"脏"。Minor GC时,只需扫描脏卡而非整个老年代,大大加快了速度。
  3. Serial、ParNew、Parallel Scavenge收集器各自的特点和适用场景是什么?

    • 核心分析Serial :单线程,简单高效,适用于客户端或小内存单核环境。ParNew :Serial的多线程并行版本,是CMS在新生代的默认搭档。Parallel Scavenge("吞吐量优先"收集器):目标是达到可控制的吞吐量(运行用户代码时间/总时间),适用于后台计算任务。
  4. CMS(Concurrent Mark-Sweep)收集器的工作流程是怎样的?它有什么优点和缺点?

    • 核心分析 :流程:① 初始标记(STW快)② 并发标记 ③ 重新标记(STW)④ 并发清除。优点 :并发,低停顿。缺点:对CPU敏感;无法处理浮动垃圾;会产生内存碎片;可能引发"Concurrent Mode Failure"导致Full GC。
  5. G1(Garbage-First)收集器是如何工作的?相比CMS有哪些改进?

    • 核心分析 :G1将堆划分为多个大小相等的Region ,并跟踪每个Region的垃圾价值(回收所得空间及所需时间)。工作流程:初始标记 -> 并发标记 -> 最终标记 -> 筛选回收(STW,选择价值最高Region回收)。改进 :① 整体基于"标记-整理",局部基于"复制",避免内存碎片 。② 可预测的停顿时间模型 (通过-XX:MaxGCPauseMillis设定)。③ 能独立管理整个堆。
  6. ZGC和Shenandoah作为新一代低延迟收集器,其核心设计目标是什么?它们是如何实现极低停顿时间的(如读屏障、染色指针等技术思想)?

    • 核心分析 :核心目标:将STW停顿时间控制在10ms以内 ,且几乎与堆大小无关。关键技术染色指针 (将GC信息直接存储在对象指针中,而非对象头,使得某些阶段无需访问对象即可完成操作)、读屏障 (在读取指针时触发一些操作,如对象转移的转发)。它们实现了并发标记、并发转移,大大减少了STW。
  7. 如何选择适合的垃圾收集器?(从应用场景、停顿要求、吞吐量等角度分析)

    • 核心分析 :① 小型应用/单核:Serial。② 追求吞吐量(如数据分析):Parallel Scavenge + Parallel Old。③ 追求低延迟(如Web应用):JDK8可选ParNew + CMS;JDK11+推荐G1;超大堆或超低延迟(<10ms)选ZGCShenandoah。需结合-XX:+Use*GC参数和具体硬件考量。
  8. 什么情况下会触发Minor GC、Major GC(Full GC)?

    • 核心分析Minor GC :Eden区空间不足时触发。Major GC/Full GC :定义较混乱,通常指回收整个堆(包括年轻代和老年代)。触发条件:老年代空间不足;方法区空间不足;调用System.gc()(建议);CMS GC时出现"Concurrent Mode Failure";堆内存分配超大对象时空间不足等。
  9. GC日志中,[GC (Allocation Failure) ...][Full GC (System.gc()) ...] 分别表示什么?如何分析GC日志?

    • 核心分析Allocation Failure表示对象分配失败触发的GC,最常见。System.gc()表示由代码显式调用触发。分析GC日志需关注:时间戳、GC类型、回收前后各区域容量变化、耗时 。关键指标:GC频率、停顿时间、吞吐量、内存使用率。可使用gceasy等在线工具辅助分析。
  10. 常用的GC调优参数有哪些?(如堆大小、新生代比例、晋升阈值等)

    • 核心分析 :基础:-Xms/-Xmx(堆初始/最大)、-Xmn(新生代大小)、-XX:SurvivorRatio(Eden/Survivor比例)、-XX:MaxTenuringThreshold(晋升阈值)。CMS:-XX:CMSInitiatingOccupancyFraction(触发回收阈值)。G1:-XX:MaxGCPauseMillis(目标暂停时间)、-XX:G1HeapRegionSize(Region大小)。
  11. 如何排查和解决Java应用中的内存泄漏(Memory Leak)问题?

    • 核心分析 :步骤:① 监控发现异常(如GC后堆内存不降、OOM)。② 使用jpsjstat初步定位。③ 使用jmap -dump:format=b,file=heap.hprof <pid> 生成堆转储。④ 使用MAT、JProfiler等工具分析堆快照,查找大对象、支配树、GC Roots引用链,定位泄漏对象和代码。
  12. 什么是"并发模式失败"(Concurrent Mode Failure)?如何避免?

    • 核心分析 :发生在CMS收集器并发清理阶段,此时应用线程仍在运行并产生新垃圾(浮动垃圾),如果老年代空间无法满足这些新对象,就会发生CMF,此时JVM会启用Serial Old收集器进行Full GC (长时间STW)。避免 :合理设置-XX:CMSInitiatingOccupancyFraction,预留足够空间。
  13. 什么是"分配担保"(Handle Promotion Failure)?

    • 核心分析 :发生在Minor GC前,JVM会检查老年代最大可用连续空间 是否大于新生代所有对象总空间历次晋升到老年代对象的平均大小。如果条件不成立,则可能触发一次Full GC以确保安全。这是担保Minor GC后存活对象能顺利进入老年代的机制。
  14. -XX:+DisableExplicitGC 参数有什么作用?为什么生产环境有时会启用它?

    • 核心分析 :禁用System.gc()调用。启用原因:① System.gc()会触发Full GC,造成不可预测的停顿。② 一些框架/库(如NIO的Direct Buffer清理)或RMI会隐式调用它。启用后需确保应用不依赖显式GC来管理内存(如堆外内存),或使用-XX:+ExplicitGCInvokesConcurrentSystem.gc()触发并发GC。
  15. 如何理解"大对象直接进入老年代"?相关参数是什么?

    • 核心分析 :大对象(如长数组、大字符串)需要连续内存空间,容易导致提前触发GC且产生大量内存拷贝。通过-XX:PretenureSizeThreshold参数(只对Serial和ParNew收集器有效),可以设定对象大小阈值,超过此阈值的对象直接在老年代分配,避免在新生代反复复制。
  16. 什么是"动态对象年龄判定"?

    • 核心分析 :HotSpot并非永远要求对象年龄达到MaxTenuringThreshold才晋升。如果在Survivor空间中相同年龄所有对象大小的总和大于Survivor空间的一半 ,那么年龄大于或等于该年龄的对象就可以直接进入老年代。这是一种自适应的优化策略。
  17. G1收集器中的"Mixed GC"是什么?

    • 核心分析 :Mixed GC是G1特有的阶段。在并发标记完成后,G1知道哪些Region垃圾最多。Mixed GC会同时回收一部分年轻代Region和一部分标记为垃圾多的老年代Region。它是介于Minor GC(只回收年轻代)和Full GC(回收整个堆)之间的一种回收。
  18. ZGC中的"染色指针"(Colored Pointers)技术具体是如何工作的?它有什么优势?

    • 核心分析 :ZGC利用指针的64位中未使用的位(在Linux下是42-45位)来存储GC状态信息(如标记、转发、重映射)。优势:① 使得某些GC阶段(如标记、转移)可以仅通过操作指针完成,无需访问对象内存,更快 。② 支持并发转移,极大地减少了STW时间。
  19. 如何监控JVM的GC状态和性能?你用过哪些工具?

    • 核心分析 :命令行:jstat -gc <pid> 实时查看各区域容量和GC次数/时间。可视化:jconsoleVisualVMJMC(Java Mission Control)。生产级APM:Arthas(阿里)、Prometheus + Grafana(监控指标)。需结合应用日志和系统监控综合判断。
  20. 什么是"安全点"(Safepoint)和"安全区域"(Safe Region)?它们对GC有何意义?

    • 核心分析安全点 :代码中一些特定的位置(如方法调用、循环跳转),线程执行到这些位置时,其状态(如栈帧、寄存器)是确定的,可以安全地进行GC等操作。安全区域 :在一段代码片段中,引用关系不会发生变化,线程在此区域任意点开始GC都是安全的。它们是实现所有GC器STW的协作机制

💡 类加载机制(8题)

这部分考察你对Java动态性的底层支持的理解。

  1. 类加载的过程分为哪几个步骤?

    • 核心分析 :五个阶段:① 加载 :获取字节流,生成Class对象。② 验证 :确保字节流安全合规。③ 准备 :为静态变量分配内存并设初始值(零值)。④ 解析 :将符号引用转为直接引用。⑤ 初始化 :执行<clinit>方法(静态块和静态变量赋值)。其中加载、验证、准备、解析属于连接阶段。
  2. 什么是双亲委派模型(Parents Delegation Model)?请描述其工作流程及其好处。

    • 核心分析 :工作流程:一个类加载器收到加载请求时,先委派给父加载器 ,依次向上。只有当所有父加载器都无法完成时,自己才尝试加载。好处 :① 保证核心类安全 :防止用户自定义类篡改核心类(如java.lang.String)。② 避免重复加载:保证类的全局唯一性。
  3. 有哪些场景破坏了双亲委派模型?是如何破坏的?(如JDBC、Tomcat)

    • 核心分析 :破坏场景:① 历史原因 :JDK1.2前无该模型,为了兼容,loadClass()方法可被子类重写。② 自身缺陷 :基础类(如java.sql.Driver)需调用由应用类加载器实现的SPI代码(如数据库驱动),使用线程上下文类加载器 反向委派。③ 模块化热部署:如Tomcat为每个Web应用提供独立类加载器,实现应用隔离。
  4. 如何自定义一个类加载器?需要重写哪些方法?

    • 核心分析 :继承ClassLoader类。通常只需重写findClass(String name)方法(遵循双亲委派),在该方法中根据名称找到或生成字节码,最后调用defineClass()将字节数组转换为Class对象。不应轻易重写loadClass()方法,除非明确要破坏双亲委派
  5. <clinit><init> 方法有什么区别?

    • 核心分析<clinit>类构造器 ,由编译器自动收集所有类变量的赋值动作和静态语句块 合并而成,在类初始化 时执行,只执行一次。<init>实例构造器 ,在对象实例化时执行,可能有多份(对应不同构造器)。两者都是线程安全的。
  6. 什么情况下会触发一个类的初始化?(主动引用 vs 被动引用)

    • 核心分析主动引用 (触发初始化):new、读写静态字段(非常量)、调用静态方法、反射、初始化子类(先初始化父类)、作为主类。被动引用 (不触发初始化):通过子类引用父类静态字段;通过数组定义引用类;引用编译期常量(static final常量,值已在编译期确定,存入常量池)。
  7. Tomcat等Web容器是如何实现Web应用隔离的?(从类加载器角度)

    • 核心分析 :Tomcat为每个Web应用创建一个**WebAppClassLoader实例。它 优先加载自己/WEB-INF/classes/WEB-INF/lib下的类**(打破双亲委派,实现应用隔离),对于基础类库(如java.*javax.*)则委派给共同的父加载器(如Common ClassLoader)加载,实现共享。这保证了不同应用间类库的独立与共享。
  8. 什么是"命名空间"(Namespace)?两个类"相等"的条件是什么?

    • 核心分析 :每个类加载器实例都有独立的命名空间。一个类的唯一性由其全限定名和加载它的类加载器实例 共同决定。即使两个类来自同一个.class文件,被不同的类加载器加载,在JVM看来也是两个完全不同的类instanceofequals()isAssignableFrom()等都会返回false

💡 执行引擎与性能调优(8题)

这部分将知识与实际性能问题关联,考察综合能力。

  1. 解释执行和即时编译(JIT)有什么区别?HotSpot虚拟机是如何结合这两种方式的?

    • 核心分析解释执行 :逐条翻译执行字节码,启动快,执行慢。JIT编译 :将"热点代码"(频繁执行的代码)编译成本地机器码,执行快,但需要编译时间。HotSpot采用混合模式:程序启动时以解释器为主,随着运行,热点代码被编译,后续执行直接使用编译后的本地代码。
  2. 什么是热点代码?JVM如何探测热点代码?

    • 核心分析 :热点代码指被频繁执行的方法或代码块 。探测方式:基于计数器 (方法调用计数器和回边计数器)。当一个方法被调用的次数达到阈值,或一个循环体的循环次数达到阈值,就会触发JIT编译。-XX:CompileThreshold可设置阈值。
  3. JIT编译器有哪些常见的优化技术?(如方法内联、逃逸分析、公共子表达式消除等)

    • 核心分析 :① 方法内联 :将目标方法代码"复制"到发起调用的方法中,消除调用开销。② 逃逸分析 (见第14题)。③ 公共子表达式消除 :如果一个表达式之前已经计算过,且变量值未变,则直接用结果替换。④ 数组边界检查消除 :在安全情况下省略数组下标检查。⑤ 锁消除/锁粗化
  4. -client-server 模式(已过时但体现思想)下JVM的行为有什么主要区别?

    • 核心分析 :体现不同的编译策略取舍。-client(C1编译器):启动速度快 ,编译优化较少。-server(C2编译器):启动较慢 ,但进行更多的激进优化 (如深度内联、逃逸分析),追求峰值性能。现代多核服务器环境默认使用服务端模式。
  5. 什么是"代码缓存"(Code Cache)?它满了会怎样?

    • 核心分析 :JVM用于存放JIT编译生成的本地机器码 的内存区域。如果满了,JVM会停止JIT编译,后续代码只能解释执行,导致性能下降。可以调整大小:-XX:ReservedCodeCacheSize-XX:InitialCodeCacheSize。可以使用-XX:+PrintCodeCache监控使用情况。
  6. 你常用的JVM性能监控和故障处理工具有哪些?分别用在什么场景?

    • 核心分析 :需形成工具箱思维。① 基础诊断jps(查进程)、jstat(GC/类加载统计)、jmap(堆转储)、jstack(线程快栈查死锁)。② 可视化/分析jconsoleVisualVMMAT(分析堆转储)。③ 线上诊断Arthas (阿里,功能强大,动态跟踪、热更新等)。④ 监控Prometheus + Grafana
  7. 如果发现系统CPU使用率很高,且是Java进程导致的,你的排查思路是什么?

    • 核心分析 :① top找到高CPU的Java进程及其线程。② top -Hp <pid>找到该进程中高CPU的线程ID。③ 将线程ID转为16进制(printf "%x\n" <tid>)。④ jstack <pid> > stack.log抓取线程快照。⑤ 在stack.log中搜索16进制线程ID,定位线程堆栈,分析是计算密集型代码死循环 还是GC问题 (结合jstat -gc)。
  8. 给你一个正在运行的、存在性能问题(如周期性卡顿)的Java应用,你的一般性调优思路和步骤是怎样的?

    • 核心分析 :体现系统性方法论。① 确立指标 :明确问题(吞吐量下降?延迟增高?)。② 监控收集 :收集GC日志、线程快照、堆转储、系统指标(CPU、内存、IO)。③ 分析定位 :分析数据,定位瓶颈(如频繁Full GC、锁竞争、内存泄漏)。④ 制定方案 :调整JVM参数(堆大小、收集器)、优化代码(避免内存泄漏、减少锁粒度)。⑤ 验证迭代 :在预发环境测试,对比指标,持续优化。强调"数据驱动"和"假设验证"

💎 从理论到实践的建议

你已经掌握了JVM面试的知识图谱。为了将这些知识内化为能力,我建议:

  1. 动手实验 :在本地或测试环境,使用 -XX:+PrintGCDetails-Xlog:gc* 等参数运行程序,亲自分析GC日志。尝试用 jmapMAT 分析一个简单的内存泄漏程序。
  2. 参数调优:为一个简单的Spring Boot Web应用,尝试使用G1、ZGC等不同收集器,并通过压测工具观察其吞吐量和停顿时间的变化,理解参数的实际影响。
  3. 源码关联(高阶):如果感兴趣,可以阅读《深入理解Java虚拟机》中关于HotSpot源码的章节(如垃圾收集器的实现),这将极大提升你的原理深度。

JVM的学习是一个长期过程。如果你在后续的学习或实践中,对 "生产环境JVM调优案例"、"特定中间件(如Kafka, Elasticsearch)的JVM配置"或"容器化(Docker/K8s)环境下的JVM挑战" 等更具体的方向有疑问,我们可以继续深入探讨。

相关推荐
我居然是兔子5 小时前
Java虚拟机(JVM)内存模型与垃圾回收全解析
java·开发语言·jvm
@淡 定5 小时前
JVM 问题排查手段
jvm
小马爱打代码6 小时前
实战:CPU被打满100%,如何处理
jvm·cpu·排查故障
程序员阿鹏8 小时前
OOM是如何解决的?
java·开发语言·jvm·spring
是一个Bug9 小时前
JVM基础50道经典面试题(二)
jvm
_李小白12 小时前
【Android FrameWork】第三十四天:系统设置项(Settings)与系统属性(System Properties)
android·jvm·oracle
小CC吃豆子12 小时前
JVM-垃圾回收
jvm
没有bug.的程序员12 小时前
负载均衡的真正含义:从算法到架构的深度解析
java·jvm·算法·微服务·架构·负载均衡
Knight_AL13 小时前
一次真实 GC 实验:Parallel 与 G1 在 `Xms < Xmx` 下的日志对比分析
jvm