jar启动卡死问题分析总结

问题

java -jar content.jarJDK21 环境下启动出现卡死/长时间无日志(CPU 高),同一份代码打出来的另一个 jar 不会卡死。

分析过程

  • 基础对比 :对比两份 jar 的 MANIFEST.MFMain-Class/Start-Class 一致;解压后文件结构一致。
  • 依赖一致性校验 :对比两边 BOOT-INF/lib148 个依赖 jar ,按 SHA256 逐文件校验 ,结论为完全一致(无缺失、无版本不一致、无同名不同内容)。
  • 定位卡死点 :抓取卡死进程线程栈,main 线程卡在 Spring Boot loader:
    • org.springframework.boot.loader.zip.ZipString.hash
    • ZipContent$Loader.loadContent
      说明卡死发生在 解析外层 jar 的 ZIP 目录/索引阶段,尚未进入业务启动逻辑。
  • 定位 jar 的关键差异 :解析两份 jar 的 ZIP entry extra fields,发现卡死 jar 大量包含:
    • extra header id=0x5455(ZIP 的 UT/Extended Timestamp
    • 996 个 entry 带 UT;不卡死 jar 不带 UT
  • 复现验证 :按构建方式(JDK21 + build 目录 mvn clean install)重新打包,产物再次出现 UT=996 ,启动复现卡死;调整打包输出时间戳后,验证可将 UT 降为 0,启动不再卡死并能正常输出日志。

说明:另一个 jar 启动时出现的 IllegalAccessError(模块强封装导致的 --add-opens 问题)属于不同问题,不作为本次"卡死"根因。

分析结论

  • 卡死原因不是依赖包/版本不一致 ,因为 BOOT-INF/lib 完全一致。
  • 卡死根因是:打包产物的 ZIP 元数据中写入了大量 UT(0x5455) 扩展时间戳 ,触发 Spring Boot loader 在解析 ZIP 内容时走到极慢路径,表现为卡在 ZipString.hash
  • UT 的出现与打包过程相关(归档条目时间戳/归档实现差异),而不是业务代码本身。

解决方案

  • 工程级(推荐) :在父 pom.xml 增加 Maven 属性,固定归档输出时间戳,避免 UT 写入并让产物稳定可复现:
xml 复制代码
<properties>
  <!-- 固定归档条目时间戳,避免 ZIP 扩展时间戳(UT/0x5455)写入 -->
  <project.build.outputTimestamp>1980-02-01T00:00:00Z</project.build.outputTimestamp>
</properties>
  • 验证结果 :重新 mvn clean install 后,产物 UT(0x5455)=0,启动不再卡死。
  • 可选:全局生效 :也可在 ~/.m2/settings.xml 的 profile 中注入同名属性并默认启用,让本机所有构建生效;但仍建议保留在项目 pom.xml,避免 CI/他人环境复现问题。示例:
xml 复制代码
<settings xmlns="http://maven.apache.org/SETTINGS/1.2.0"
          xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
          xsi:schemaLocation="http://maven.apache.org/SETTINGS/1.2.0 https://maven.apache.org/xsd/settings-1.2.0.xsd">
  <profiles>
    <profile>
      <id>reproducible-jar</id>
      <properties>
        <project.build.outputTimestamp>1980-02-01T00:00:00Z</project.build.outputTimestamp>
      </properties>
    </profile>
  </profiles>

  <!-- 默认启用(也可以不写这里,改为构建时加 -Preproducible-jar) -->
  <activeProfiles>
    <activeProfile>reproducible-jar</activeProfile>
  </activeProfiles>
</settings>
  • 可选:命令行动态覆盖(不推荐用于解决卡死) :只对本次构建 生效,可以用 -D 覆盖 project.build.outputTimestamp
bash 复制代码
mvn clean install -Dproject.build.outputTimestamp="$(date -u +%Y-%m-%dT%H:%M:%SZ)"

说明:

  • 这会让每次构建的"归档条目时间戳"随当前时间变化。
  • 由于本问题与 ZIP 的 UT(0x5455) 扩展时间戳强相关,动态时间戳可能导致 UT 再次写入,从而重新触发启动卡死风险。
相关推荐
程序员老乔1 分钟前
Java 新纪元 — JDK 25 + Spring Boot 4 全栈实战(二):Valhalla落地,值类型如何让电商DTO内存占用暴跌
java·spring boot·c#
SuniaWang8 分钟前
《Spring AI + 大模型全栈实战》学习手册系列· 专题二:《Milvus 向量数据库:从零开始搭建 RAG 系统的核心组件》
java·人工智能·分布式·后端·spring·架构·typescript
张小洛10 分钟前
Spring 常用类深度剖析(工具篇 02):ReflectionUtils——优雅操作反射的利器
java·后端·spring·工具类·spring常用类
GoodStudyAndDayDayUp27 分钟前
RUO-VUE-PRO权限关联sql
java·数据库·sql
码界奇点29 分钟前
基于Spring Boot和MyBatis的图书管理系统设计与实现
spring boot·后端·车载系统·毕业设计·mybatis·源代码管理
⑩-42 分钟前
RabbitMQ 架构和工作原理?RabbitMQ 延迟队列如何实现?
java·分布式·架构·rabbitmq
子非鱼@Itfuture44 分钟前
try-catch和try-with-resources区别是什么?try{}catch(){}和try(){}catch(){}有什么好处?
java·开发语言
Nyarlathotep01131 小时前
线程创建和Thread类
java
阿波罗尼亚1 小时前
JDK17 新特性
java
独自破碎E1 小时前
【面试真题拆解】Spring事务机制
java·spring·面试