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
环境信息
- linux: CentOS Linux release 7.4.1708
- openjdk11:openjdk version “11.0.8” 2020-07-14 LTS
- springcloud: Hostion
排查过程
- 包冲突?
使用 mvn dependency:tree 发现并没有冲突的包
解压fatjar包,发现lib下也没有冲突的jar包 - 权限未开启?
本轮发布有精简包的操作,排除掉了部分依赖包;检查了相应policy文件,未发现异常 - remote debug环境信息
发现堆栈位置,当前的classloader为 ,正常应该是
问题定位清楚
根本原因
- 并发流底层使用ForkJoinPool,在jdk11环境下,ForkJoinPool创建的线程对应的classloader为 ()
- ()默认返回
- 加载的范围是classpath,所以没有找到 fatjar 下面的
解决方案(任选其一)
- 把并发流调用方式,改成串行流
- 每次并发流调用前,设置ThreadContextClassLoader,(记得执行完恢复成之前的classloader,在springboot fatjar方式下,LaunchedURLClassLoader作为AppClassLoader,即使没有恢复成appClassLoader,业务上也没有影响,可能会有多种loader并存的怪异现象)
- 并发流执行时,使用自定义的线程池
其他依赖ForkJoinPool的场景
- 主动调用ForkJoinPool的方法
参考
- java -jar启动机制:/u012702547/article/details/99543936
- springboot启动机制:/kangjnghang/article/details/107046925
- /spring-cloud/spring-cloud-netflix/issues/3101
- https:///