从 finalize 到 Cleaner:JVM 终结机制的可达性判定与清理线程调度
封装本地内存或文件句柄的资源类,为什么不能依赖 finalize 兜底?文章从可达性判定出发,解释终结队列、FinalizerThread 与对象复活带来的不确定延迟,再拆解 Cleaner 如何用 PhantomReference、Cleanable 和独立清理线程替代终结机制,并给出与 try-with-resources 的适用边界。
深入解析 Java、JVM、Spring、并发编程与服务端工程实践。
封装本地内存或文件句柄的资源类,为什么不能依赖 finalize 兜底?文章从可达性判定出发,解释终结队列、FinalizerThread 与对象复活带来的不确定延迟,再拆解 Cleaner 如何用 PhantomReference、Cleanable 和独立清理线程替代终结机制,并给出与 try-with-resources 的适用边界。
高并发订单服务在突发流量下出现秒级停顿,GC 日志里往往能看到疏散失败与 Full GC 连续出现。本文从 G1 混合回收的复制过程出发,说明疏散失败的判定条件、失败后对象就地保留的机制、G1EvacFailureRegions 与保留空间参数的作用,并给出识别失败信号与调整保留空间的判断方法。
容器镜像升级后 Java 应用启动变慢,常见原因是 CDS/AppCDS 归档与新的类路径或 JDK 不匹配,而 -Xshare:auto 会静默回退到普通类加载。本文解释归档映射与类匹配校验过程,给出用 -Xlog:cds 和 -Xshare:on 定位未命中的方法,并说明归档重建与版本绑定的判断依据。
面向 TB 级堆低延迟服务,解释 ZGC 为何把同一份物理堆映射成 Marked0、Marked1、Remapped 三个虚拟视图,彩色指针如何借视图编码标记状态,读屏障在加载引用时做什么,以及多重映射带来的虚拟地址与常驻内存开销。同时对比 Shenandoah 的 Brooks 指针方案,给出地址空间规划与诊断要点。
长时间运行的服务里,代码缓存耗尽会让 JIT 编译线程停摆,性能悄悄退化。文章从 HotSpot 代码缓存的分段布局出发,解释 CodeBlob、nmethod 与各段边界的关系,说明 JEP 197 引入分段后清扫策略和编译线程行为的变化,并给出按编译层级分布调整各段容量的判断方法。
面向 TB 级堆的低延迟交易服务,解释 JDK 21 引入的分代 ZGC 如何通过彩色指针、读屏障与写屏障把标记工作从读路径搬到写路径,让年轻代高频回收与老代并发整理分工协作,并对比非分代 ZGC 与 G1 的吞吐、延迟和内存开销,给出选型边界与失败模式。
订单服务里内部方法调用导致 @Transactional 不生效,是 Spring AOP 代理边界的典型表现。文章从 JDK 动态代理与 CGLIB 的生成方式讲起,解释 this 与代理对象的区别、事务与缓存切面为何被绕过,并对比自注入、AopContext、拆分 Bean 与 AspectJ 织入的适用条件与代价。
高并发订单服务出现周期性长停顿,根因常被误判为堆太大或对象太多。本文从 G1 写屏障的入队动作出发,解释脏卡队列如何通过阈值与 mutator 协作形成背压,说明并发 refinement 线程的处理路径,并给出监控队列长度、调整相关参数与定位 RSet 膨胀的诊断方法。
高并发服务中短生命周期对象常被当作 GC 压力的来源,但 C2 的逃逸分析与标量替换能在编译期消除一部分分配。本文解释 NoEscape、ArgEscape、GlobalEscape 的判定过程,标量替换的成立条件,与 TLAB 的关系,以及哪些代码写法会让对象无法被拆解。
高吞吐消息中间件里,批量字节拷贝常依赖 System.arraycopy 获得接近 memcpy 的性能,但这一收益只在 C2 完成内在化、且地址对齐与类型检查通过时才成立。文章拆解 HotSpot 从 intrinsic 识别到 CPU 指令生成的路径,说明手写循环为何难以匹敌,以及哪些条件会让它退回运行时调用。
面向高并发 Java 服务,解释 G1 的 IHOP 如何决定并发标记启动时机,为什么默认 45% 在新生代占比过高或老年代增长过快时会失效,以及如何结合堆增长速率与 G1HeapWastePercent 调整阈值来避免 Full GC。
高吞吐服务里热点分支被 C2 编译成条件传送(CMov)后反而变慢,问题常出在分支概率与代价模型上。文章解释 C2 如何用分支概率决定是否把 if 转成 CMov、CMov 为什么在概率极不平衡时付出额外代价,并给出用 -XX:+PrintIdeal 与反汇编判断分支是否适合 CMov 的方法。
高并发订单服务中,写屏障产生的跨 Region 引用更新会先进入 Dirty Card Queue,再由并发 refinement 线程异步写入 RSet。本文解释这一异步链路的完整生命周期、refinement 线程与 mutator 的竞争关系,以及 -XX:G1RefinementThreads 等参数在吞吐与停顿之间的取舍,并给出监控与排查方法。
订单事务提交后发送消息或清缓存时,为什么回调有时读不到数据、有时又让事务回滚?本文拆解 TransactionSynchronizationManager 的 ThreadLocal 资源绑定、同步注册与触发顺序,说明 beforeCommit 抛异常与 afterCommit 抛异常的差别,并给出可观测与降级思路。
本文聚焦 G1 GC 对超过区域大小一半的巨型对象的处理机制,解释其为何直接进入老年代并可能引发提前 Full GC,以及如何通过调整区域大小(-XX:G1HeapRegionSize)优化。读者将理解巨型对象的分配路径、回收策略与工程权衡,并获得避免负面影响的配置方法。
本文聚焦 HotSpot 的 TLAB 机制,解释为何小对象分配通过指针碰撞完成,以及 TLAB 大小如何影响分配效率与 GC 停顿。通过高并发服务场景,分析 TLAB 初始化、分配流程、慢路径分配、参数调优及其与逃逸分析的关系,帮助读者理解并优化 Java 应用的对象分配性能。
为什么 Math.sqrt 和 System.arraycopy 会被 JIT 替换为 CPU 指令?本文聚焦 HotSpot C2 编译器的内在函数机制,解释识别与替换过程、常见内在函数、诊断方法,以及手写代码与内在函数的性能差异,帮助你利用内在函数优化数值计算和数组操作。
本文以接口多实现的高频调用为场景,剖析 HotSpot 内联缓存的单态、多态与巨态状态转换机制,解释多态退化为何损失性能,以及如何通过 -XX:+PrintInlining 观察和避免退化。
本文聚焦 G1 GC 的字符串去重功能,解释其如何通过共享字符数组降低堆内存占用,并分析并发去重线程与 SATB 的交互、哈希表维护机制,以及 -XX:+UseStringDeduplication 和 -XX:StringDeduplicationAgeThreshold 参数。读者将获得根据内存收益和 CPU 开销决定是否启用去重的判断方法。
本文围绕订单创建后发送通知与更新统计的场景,解析 Spring 事件发布从 publishEvent 到监听器执行的同步调用链,说明 @TransactionalEventListener 如何绑定事务阶段,以及异步监听器与事务事件结合时的陷阱。读者将学会正确使用事件机制,避免事务不一致。
本文聚焦 Java 22 正式化的 Foreign Function & Memory API,对比 JNI 与 FFM 在本地函数调用上的机制差异。通过一个调用 C 库数值计算函数的贯穿场景,解释 downcall 与 upcall、内存段与作用域、Panama ABI 等核心概念,并给出迁移决策依据。
本文聚焦 Java 9 引入的 StackWalker API,解决日志框架中高效获取调用者类名的问题。通过对比 Throwable.getStackTrace 的完整快照机制,解释 StackWalker 的惰性流式遍历、安全限制与适用边界,帮助读者在栈遍历场景中做出合理的技术选型。
本文聚焦 G1 GC 并发标记阶段的 SATB 队列机制,解释其如何通过写前屏障记录旧引用,保证快照一致性,避免漏标。结合高并发订单服务场景,分析 SATB 与增量更新的区别、浮动垃圾的产生,以及如何通过日志和 JFR 观察标记进度。
高吞吐服务中,热点分支是保留分支指令还是转换为条件传送(CMov)直接决定性能。本文基于 HotSpot C2 编译器的实际实现,剖析 CMov 的收益与代价、C2 如何利用分支概率决定转换,以及 -XX:UseCMov 等参数的影响,帮助读者判断何时该信任 CMov 的优化。
为什么有的循环能被 JVM 自动向量化,有的却不能?本文以数组求和与元素变换为贯穿场景,剖析 HotSpot C2 编译器 Superword 算法的基本步骤,解释循环展开、内存对齐、无别名等关键条件,并给出编写可向量化代码的实用指导,同时对比自动向量化与手写 SIMD 的权衡。
长时间运行的应用中,代码缓存膨胀可能导致 JIT 编译被禁用,性能急剧下降。本文从 nmethod 的生命周期出发,解释 HotSpot 清扫机制的触发条件、分段代码缓存如何缓解碎片化,以及如何通过 -XX:ReservedCodeCacheSize 和 -XX:+UseCodeCacheFlushing 等参数进行监控与调整。
本文围绕 HotSpot 压缩指针(Compressed OOPs)的零基址(Zero-Based)编码展开,解释为何堆超过 32GB 时压缩指针失效,以及如何通过-XX:ObjectAlignmentInBytes 调整对齐边界。结合容器化微服务的内存规划场景,给出堆大小、对齐参数与压缩模式的决策方法。
本文深入解读 Spring @Cacheable 的 sync 属性如何通过同步控制防止缓存击穿,重点分析在高并发下 sync=true 为何可能导致锁竞争,以及如何与 Redis 等缓存提供者的原子操作配合。通过热点数据过期场景,读者将理解 Spring Cache 拦截器的同步机制、不同缓存提供者的锁实现,以及通过自定义 CacheErrorHandler 处理异常,从而获得避免缓存击穿和锁问题的配置方法。
本文剖析 Spring @Async 默认线程池的拒绝策略,重点讨论 CallerRunsPolicy 在任务堆积时如何导致调用线程阻塞、上下文丢失甚至死锁。通过一个高并发异步任务堆积的贯穿场景,解释 ThreadPoolTaskExecutor 的配置与拒绝策略行为,并给出自定义拒绝策略和上下文传播的解决方案,帮助读者根据任务特性选择合适的拒绝策略。
本文解读 JDK 10 引入的线程局部握手机制,说明它如何在不触发全局安全点的前提下对单个线程执行 JIT 去优化、偏向锁撤销等操作,并通过与全局安全点的对比和诊断参数帮助读者理解其适用场景与局限。
本文分析 HotSpot C2 编译器如何根据方法大小、调用频率和调用深度决定内联,解释为何某些热点方法未被内联,并演示如何通过 -XX:MaxInlineSize 等参数优化。以微服务中频繁调用的工具方法为贯穿场景,介绍内联收益与成本、虚方法去虚拟化技术,以及使用 -XX:+PrintInlining 诊断内联失败的方法,帮助读者平衡编译时间与运行时性能。
本文解读 JVM 11 引入的 CONSTANT_Dynamic 常量池条目,通过 Bootstrap 方法实现常量按需解析,减少类加载开销与内存占用。结合大型框架中条件性常量的场景,解释常量池结构、引导方法调用机制,以及与 invokedynamic 的关系,并给出实践方法。
Shenandoah GC 通过并发疏散机制将对象移动与 Java 线程并行执行,从而将停顿时间控制在亚毫秒级。本文聚焦 Brooks 指针与读屏障如何实现“移动对象时不暂停应用线程”这一核心问题,以高并发 Web 服务在大型堆下的停顿优化为场景,逐步展开并发标记、并发疏散、引用更新等阶段的工程细节,并与 ZGC 进行对比,为读者提供吞吐与延迟之间权衡的决策依据。
分析 HotSpot 分层编译的五个执行层级,解释为何方法在 C1 编译后可能被 C2 重新编译,以及如何通过 -XX:TieredStopAtLevel 控制编译策略。以微服务启动后预热期的性能抖动为贯穿场景,讨论编译阈值、Profiling 数据收集、去优化机制,并与 Graal JIT 进行对比。读者将掌握根据延迟和吞吐需求调整编译层级的参数,并理解为何短生命周期方法不适合 C2 编译。
当多个模块导出同名包时,JPMS 会因分裂包而拒绝启动。本文以大型应用从类路径迁移到模块路径为场景,解释模块解析过程中包唯一性约束的由来,剖析导致分裂包的常见依赖结构,并给出通过模块描述符、构建工具及命令行选项诊断与修复问题的具体步骤。
本文分析 Java 类加载器泄漏的典型模式,以 Spring Boot DevTools 频繁重启为贯穿场景,解释元空间持续增长直至 OOM 的根本原因。文章梳理类加载器引用链、ThreadLocal 残留、静态字段持有等关键泄漏路径,并结合 Eclipse Memory Analyzer 演示诊断步骤。读者将掌握识别和修复类加载器泄漏的方法,理解为何关闭线程池和清除上下文是防止泄漏的核心措施。
本文剖析 Spring 声明式事务中传播行为(特别是 REQUIRES_NEW)与数据库隔离级别的组合效应。通过订单创建与库存扣减的跨服务事务编排场景,解释 Spring 事务管理器如何映射传播行为到 JDBC 连接、保存点与事务同步,并分析不同隔离级别下的锁行为与异常路径。读者将获得避免事务边界错误和性能问题的决策矩阵,并理解为何默认隔离级别可能不适用于高并发写操作。
容器化微服务偶发长停顿的根因往往指向 JVM 安全点机制。本文以一次典型 GC 停顿超时排查为线索,深入安全点的线程协作模型、VM 操作类型、日志与 JFR 诊断方法,并给出常见阻塞原因与优化策略,帮助读者系统掌握安全点停顿的分析与解决。
本文分析偏向锁在 Java 15 中被默认禁用并最终移除的工程原因,聚焦高并发下撤销开销如何抵消收益。以线程池频繁争用的同步块性能诊断为贯穿场景,解释偏向锁的获取、撤销、批量重偏向与批量撤销流程,并结合 JEP 374 的决策依据,说明现代应用为何应避免依赖偏向锁,以及如何通过 JFR 和锁分析工具诊断同步性能。
本文聚焦 G1 GC 如何通过区域划分和记忆集实现并发标记与部分回收,核心问题是“在低延迟目标下,记忆集维护如何影响吞吐与内存开销”。以高并发订单服务为贯穿场景,分析记忆集数据结构、写屏障、SATB 算法及调优参数,并与 CMS 和 ZGC 对比,帮助读者根据堆大小和延迟目标选择 GC 策略。
本文通过一个 JSON 反序列化场景,演示如何结合 Java 的记录类与密封类构建代数数据类型,解决类型不安全和穷举性缺失的问题,同时降低对象拷贝与相等性比较的出错率。
本文深入解析 JDK Flight Recorder 的事件流 API 如何实现生产环境下的低开销实时监控。通过一个高并发交易服务的贯穿场景,阐述事件流的内部架构、关键事件类型、与 Prometheus 等外部系统的集成方式,以及锁竞争、内存分配等细节的采集原理。文章还对比了传统 JMX 监控的局限,并讨论虚拟线程下的 pinning 检测、背压控制等常见失败模式,为构建自定义预警提供工程指导。
本文追溯 Java 模式匹配从 JEP 394 到 JEP 441 的演进,以订单处理服务为场景,展示如何利用密封类型实现穷举性 switch,消除强制转型与 if-else 链,提升代码安全性与可读性。同时探讨其与数据导向编程的契合点、常见失败模式及适用边界。
本文深入解读类数据共享(CDS)与应用类数据共享(AppCDS)的内存映射原理,通过一个多实例微服务场景贯穿全文,回答如何有效降低多实例环境下的内存占用与启动时间。文章基于 JEP 310、JEP 350 及官方文档,详细说明共享归档的生成、使用、动态归档机制、常见失败模式及调优指南,并提供与替代方案的对比。
本文分析 Spring AOT 引擎在 GraalVM 原生镜像构建中的核心机制,聚焦封闭世界假设下反射与动态代理的处理方式。通过一个订单查询微服务场景,贯穿静态分析、代理注册、配置提示和编译流程,并与 JVM 模式进行定性比较,帮助读者理解 Spring 原生镜像在启动耗时与内存占用上的优化原理及适用边界。
深入剖析 OpenJDK 虚拟线程的调度机制,包括载体线程挂载与卸载、ForkJoinPool 工作窃取,以及同步代码协程化的工程方法,并讨论大规模虚拟线程下的公平性与适用边界。