jdk11下ClassLoader的踩坑经历

时间:2025-03-18 13:45:02

ClassLoader的踩坑经历

    • 背景
    • 现象
    • 环境信息
    • 排查过程
    • 根本原因
    • 解决方案(任选其一)
    • 其他依赖ForkJoinPool的场景
    • 参考

背景

jdk11已发布多年(2018年发布),但在国内的使用情况却并不乐观,这次项目组顶着压力,把之前用到的oraclejdk8升级成openjdk11,期间遇到了不少问题,以下不探讨classloader机制在jdk8和jdk11的差异,根据实际遇到的场景,分析classloader的变化对spring项目的某些影响。

现象

2020-12-30 16:49:03.082 [-worker-3] INFO   - Service  failed in state STARTED; cause: : Failed to load class: []
: Failed to load class: []
	at (:68)
	at (:48)
	at $(:165)
	at /(Native Method)
	at /(:361)
	at (:1787)
	at (:161)
	at (:77)
	at (:90)
	at $ProxyDescriptor.<init>(:195)
	at .<init>(:304)
	at .<init>(:298)
	at (:59)
	at (:120)
	at (:93)
	at (:72)
	at (:210)
	at (:193)
	at ...
	at /$ForEachOp$(:183)
	at /$(:1655)
	at /(:484)
	at /$(:290)
	at /(:746)
	at /(:290)
	at /$(:1020)
	at /(:1656)
	at /(:1594)
	at /(:183)
Caused by: : Class  not found
	at (:2122)
	at (:65)
	... 36 common frames omitted 

环境信息

  1. linux: CentOS Linux release 7.4.1708
  2. openjdk11:openjdk version “11.0.8” 2020-07-14 LTS
  3. springcloud: Hostion

排查过程

  1. 包冲突?
    使用 mvn dependency:tree 发现并没有冲突的包
    解压fatjar包,发现lib下也没有冲突的jar包
  2. 权限未开启?
    本轮发布有精简包的操作,排除掉了部分依赖包;检查了相应policy文件,未发现异常
  3. remote debug环境信息
    发现堆栈位置,当前的classloader为 ,正常应该是
    问题定位清楚

根本原因

  1. 并发流底层使用ForkJoinPool,在jdk11环境下,ForkJoinPool创建的线程对应的classloader为 ()
  2. ()默认返回
  3. 加载的范围是classpath,所以没有找到 fatjar 下面的

解决方案(任选其一)

  1. 把并发流调用方式,改成串行流
  2. 每次并发流调用前,设置ThreadContextClassLoader,(记得执行完恢复成之前的classloader,在springboot fatjar方式下,LaunchedURLClassLoader作为AppClassLoader,即使没有恢复成appClassLoader,业务上也没有影响,可能会有多种loader并存的怪异现象)
  3. 并发流执行时,使用自定义的线程池

其他依赖ForkJoinPool的场景

  1. 主动调用ForkJoinPool的方法

参考

  1. java -jar启动机制:/u012702547/article/details/99543936
  2. springboot启动机制:/kangjnghang/article/details/107046925
  3. /spring-cloud/spring-cloud-netflix/issues/3101
  4. https:///