Java加载的class无法正常使用的问题

9 篇文章 0 订阅

一、背景

        两套相同的代码部署在10.10.105.90和10.10.105.90上,使用的是Tomcat进行部署。之前运行正常,最近也没更新代码操作,但是90上的一个接口突然报错。前面使用Nginx进行代理,理论上如果出问题,要么2台都出问题,要么都不出问题。  偏偏只有90出现问题,让我们百思不得其解。报错接口内容如下:

java.lang.NoClassDefFoundError: Could not initialize class org.apache.cxf.staxutils.StaxUtils with root cause
java.lang.NoClassDefFoundError: Could not initialize class org.apache.cxf.staxutils.StaxUtils

       这个是Apache CXF框架的一个类. 看着报错的意思就是这个class无法正常初始化,导致应用层代码/框架代码调用到的时候报错了。

        服务Tomcat重启了,没有解决。 最后连所在主机晚上都做了reboot,但是发现也没恢复。

二、排查过程分析

1、使用Arthas分析

        光从上面的日志,初步定位是class加载问题。我们直接上Arthas连接到Tomcat进程查看下这个类的情况, 可以看到class是已经加载进来:   

       但是使用jad进行反编译以及使用OGNL表达式,调用这个类的一个静态属性,也无法正常输出。

        后来经过资料查询,  class能加载到JVM, 不代表这个class就能正常初始化工作。 这个class要初始化,有一个环节,就是执行static静态代码块。 如果静态代码块报错,那么这个class无法成功初始化,自然也不能正常实例化这个class对象以及调用这个class。

        Apache CXF的StaxUtils这个类,确实存在static静态代码块。 那会不会是这里初始化执行失败,导致class无法正常使用呢?

2、尝试static静态代码块错误,本地验证是否现象一致

        为了验证上面的猜想,自己看了下Apache CXF StaxUtils源码的static静态代码块,但是没看到哪里有异常。 索性自己写一个简单的servlet,这个servlet接口的作用就是调用一个类Animal,这个Animal我故意在static静态代码块,写了一个1/0的算术,模拟报错。

        代码如图:

       使用Arthas连接到Tomcat进程,sc -d 查看class 以及通过OGNL表达式,调用一下Animal的静态属性,看是否正常输出,结果和线上报错一致,class可以正常加载,但是class无法正常使用。

     在查看Tomcat的运行日志信息:  报错信息和要排查的问题大致相同,报错这个Animal class无法正常初始化。

3、最后尝试重启实例是否看到类StaxUtils,第一次访问的报错信息

        Tomcat加载class的默认机制是惰性加载, 只有你调用接口访问到要使用的class才将这个class加载和初始化。 所以要看到第一条报错原因,需要重启,且再访问这个错误接口的第一次,错误应该能找到。

        最后发现确实是这个class的静态代码块出现了错误,涉及的是底层操作系统的lib库版本问题。 后面经过咨询,最近主机侧管理,有人对这台主机上面的lib库做了一版更新, 可能就是这个原因导致的。。。。,所以说,物理机的部署方式总是会存在这种问题。 如果是Docker容器化部署的方式,就能统一依赖和版本了。这种问题即使出现也很容易排查了

        后来主机侧也无法正确处理这个lib版本问题,只能将服务迁移到别的主机部署了. 硬是在这上面花费了一个星期时间也不值当。

        要不然已经运行了很久,不可能91上是正常,90异常。

三、总结

        1、class成功加载到了JVM,不代表这个class就能正常初始化使用。 因为加载正常,只是代表class没语法问题、能够通过CLASSPATH正常找到,并且加载进来而已。但是class的使用还要经过初始化的过程, 这个过程需要调用到class的static静态代码块, 所以,如果静态代码块抛出异常没有得到处理,也会导致class无法正常初始化进行使用

        2、生产环境特别是物理机部署,尽量保持环境一致,不要随意更新lib依赖库的版本或者新装其它软件。 就算是要装,也要先测试环境没问题,再更新到线上

        3、如果条件允许,尽量将自己的服务进行容器化改造,这样能避免某些主机出问题,某些主机正常运行,其根本原因就是所在的Linux环境发生了变化导致的。 如果容器化后,要么大家都出问题(这时候在测试环境应该能发现了,再把容器环境调正确就行),统一运行环境,有利于问题排查与分析。  要不然这种由于操作系统环境不同导致各种奇怪问题,其实是很难下手排查的。

  • 28
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 4
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

GEEK JUMP

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值