记一次神秘空指针问题的定位

文章讲述了作者在工作中遇到的一个奇怪的空指针问题,该问题发生在稳定运行的微服务中。经过分析日志和代码,发现错误定位在hashcode方法中的空指针异常。进一步排查发现,问题源于平台新增的鹰眼检测方法,在检测空指针异常时未捕获异常,导致死循环和内存溢出。最后,作者提醒在解决问题时要仔细检查,并对代码设计提出了反思。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

前言

昨天在工作中遇到一个很奇怪的空指针问题,接下来看下问题的定位到解决。

问题出现

问题发生在一个平时都很稳定的微服务中,负责的责任田为整个产品的框架组,该服务也是框架服务中的核心之一,因此直接被测试提了严重单,该微服务已经接近三个月没有过新代码合入。报错日志如下:

从日志中可以看到发生在Job.getJobId中,不用多想,肯定是该job对象为空了。接着贴一下问题发生处的代码:

 public static Job newJob(DirectedGraph<Job> graph, ServiceJob serviceJob, Resource resource)
    {
        Job job = new Job();//1
        job.setGraph(graph);
        job.setServiceJob(serviceJob);//2
        job.setResource(resource);//3
        return job;
    }

该段代码大致作用是将上层传入的serviceJob塞到一个新new出来的job中(虽然我现在还不知道为什么不直接用构造函数,问了原作者,说下周一看看。。。),然后可以确定serviceJob肯定不为空(前后都有判空),而且仔细看下日志,报错的位置居然发生在hashcode方法中,接着贴一下hashcode方法:

  @Override
    public int hashCode()
    {
        return new HashCodeBuilder().append(getJobId()).append(resource.getResourceLabel()).toHashCode();
    }

终于找到问题发生在哪儿了,在hashcode方法中引用了getJobId方法,此时job为空的话确实会报空指针,但是!看到上一段代码,在2处job就不应该为空了,因此问题只能发生在第一步,再但是!为什么new一个job对象会去调用hashcode方法呢???于是我们同事三个傻白萌就开始排查环境问题了,甚至研究了一些比较悬学的点(比如jre版本。。在问题发生前jre版本为1.3.40,而更新后为1.3.41;以及平台代码是否有更新。。。),在一无所获之后,盯着屏幕发了会呆,突然看到了调用栈中的如下日志:

。。。这个是昨天平台加入的鹰眼检测方法,大致就是一个检测空指针的类,开始我们都觉得空指针抛在我们这儿,肯定是我们的问题,但查看这个类的源码后才发现,是由于这个检测类引起的,大致就是在我们服务的启动脚本中会去调用鹰眼检测方法,检测是否存在可能造成空指针的代码,偏偏这个方法写的又有问题,在检测到空指针异常时没有catch住,当异常抛出后,又会起一个线程去检测,然后造成了一个死循环,导致内存溢出,而在内存溢出的时候,平台又有另外一个机制,对每一个新new出来的对象去比对hashcode,看是否可以回收。。。因而去调用了这个hashcode方法,此时job确实还没有完成构造,因此出现了上述问题。。。

总结

1、虽然这个问题的主要责任在于平台的代码,但是我相信这个newJob方法肯定是不规范的,如果在构造函数中赋值,完全可以规避这个问题,但是为什么要这样写而不用构造函数,还要去咨询下原作者,看是否有其他用意。

2、在发生问题时,一定要细心!不知道大家有没有这样的习惯,看异常日志的调用栈时,看到第三方的方法后就不会再往前看了。。。因此才会浪费这么长时间。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值