今天说的异常是一个很不常见的异常,至少我不经常见到这个异常。
首先先看下NoClassDefFoundError官方定义 :

Java Virtual Machine is not able to find a particular class at runtime which was available at compile time. If a class was present during compile time but not available in java classpath during runtime.
Java 虚拟机无法在运行时找到一个在编译时可用的特定类。如果在编译时存在类, 但在运行时 java 类路径中不可用。

最近做的一个项目,由同事到客户方部署及应用,但是期间发生一个诡异的问题:同一套代码打出的jar包在一个公司运行时会有一个NoClassDefFoundError异常抛出。起初看到这个异常,我们都认为是打得包或者依赖有问题。于是便重新打包部署,结果还是同样的问题。异常信息如下:

很诡异的问题,顺着报的错误去继续查找原因,最后将问题定位到一个线程池工具类中,部分代码如下:

其中 DEFAULT_MAX_CONCURRENT 定义如下:

private static final int DEFAULT_MAX_CONCURRENT = Runtime.getRuntime().availableProcessors() * 2;

这个线程池工具类在本地以及测试环境和线上环境一直都运行的没有问题,因为报错的异常信息指向了这个类。
考虑到在多个客户部署的都是同一套代码,只有硬件配置可能不同,而我们线程池初始化时的核心线程数依赖于硬件CPU核数,所以便猜测初始化线程池出了问题,核心线程数可能比最大线程数还大。
于是便开始追踪源码,一探究竟。

线程池初始化源码:

public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
ThreadFactory threadFactory) {
this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
threadFactory, defaultHandler);
}

继续往下看其初始化过程:

public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
ThreadFactory threadFactory,
RejectedExecutionHandler handler) {
if (corePoolSize < ||
maximumPoolSize <= ||
maximumPoolSize < corePoolSize ||
keepAliveTime < )
throw new IllegalArgumentException();
if (workQueue == null || threadFactory == null || handler == null)
throw new NullPointerException();
this.acc = System.getSecurityManager() == null ?
null :
AccessController.getContext();
this.corePoolSize = corePoolSize;
this.maximumPoolSize = maximumPoolSize;
this.workQueue = workQueue;
this.keepAliveTime = unit.toNanos(keepAliveTime);
this.threadFactory = threadFactory;
this.handler = handler;
}

上面代码可能看出,如果corePoolSize>maxPoolSize 则会抛出:IllegalArgumentException 异常,但是这和我们问题压根不一样啊?线索到这里就断了,但是至少发现了代码的一处Bug。

于是又开始沉思这个NoClassDefFoundError 异常究竟是怎么来的了,打开Oracle 文档便开始全局搜索这个,果不其然,有了新的发现:
(文档地址:https://docs.oracle.com/javase/specs/jls/se7/html/jls-12.html#jls-12.4.2)

这里意思是初始化过程时,如果这个类是用c去实现的,且初始化抛出异常时,都会对外抛出NoClassDefFoundError 异常,到了这里就很明朗了,果然是初始化线程池搞错了。
于是赶紧查看客户机器CPU核数来验证自己的猜想,果不其然,CPU为8核处理器。赶紧改了代码重新打包部署,一切到这里就结束了。

不过通过这次异常也学到了很多:
1,能不用硬编码的应该坚决杜绝,少埋这种坑。
2,多查文档,多查官方文档。

由于博主能力有限,所以如果您有更多的见解还请留言告知,不胜感激。

由初始化线程池引发的NoClassDefFoundError 异常分析的更多相关文章

  1. 启用Executor初始化线程池

    前言 上文我们介绍了JDK中的线程池框架Executor.我们知道,只要需要创建线程的情况下,即使是在单线程模式下,我们也要尽量使用Executor.即: ExecutorService fixedT ...

  2. Tomcat启动时加载数据到缓存---web.xml中listener加载顺序(例如顺序:1、初始化spring容器,2、初始化线程池,3、加载业务代码,将数据库中数据加载到内存中)

    最近公司要做功能迁移,原来的后台使用的Netty,现在要迁移到在uap上,也就是说所有后台的代码不能通过netty写的加载顺序加载了. 问题就来了,怎样让迁移到tomcat的代码按照原来的加载顺序进行 ...

  3. SpringCloud 微服务中 @Async 注解自定义线程池 引发的aop 问题

    背景 在 使用springCloud 的@Async注解来做异步操作时,想自定义其线程池. 引发问题 自定义完线程池后,发现代码里并没有使用自定义线程池里的线程,于是新建一个demo工程,一样的配置代 ...

  4. Java ThreadPoolExecutor线程池原理及源码分析

    一.源码分析(基于JDK1.6) ThreadExecutorPool是使用最多的线程池组件,了解它的原始资料最好是从从设计者(Doug Lea)的口中知道它的来龙去脉.在Jdk1.6中,Thread ...

  5. Java线程池ThreadPoolExector的源码分析

    前言:线程是我们在学习java过程中非常重要的也是绕不开的一个知识点,它的重要程度可以说是java的核心之一,线程具有不可轻视的作用,对于我们提高程序的运行效率.压榨CPU处理能力.多条线路同时运行等 ...

  6. Java线程池ThreadPoolExecutor类源码分析

    前面我们在java线程池ThreadPoolExecutor类使用详解中对ThreadPoolExector线程池类的使用进行了详细阐述,这篇文章我们对其具体的源码进行一下分析和总结: 首先我们看下T ...

  7. JUC(4)---java线程池原理及源码分析

    线程池,既然是个池子里面肯定就装很多线程. 如果并发的请求数量非常多,但每个线程执行的时间很短,这样就会频繁的创建和销毁 线程,如此一来会大大降低系统的效率.可能出现服务器在为每个请求创建新线程和销毁 ...

  8. 详解Java线程池的ctl(线程池控制状态)【源码分析】

    0.综述 ctl 是线程池源码中常常用到的一个变量. 它的主要作用是记录线程池的生命周期状态和当前工作的线程数. 作者通过巧妙的设计,将一个整型变量按二进制位分成两部分,分别表示两个信息. 1.声明与 ...

  9. JAVA线程池原理与源码分析

    1.线程池常用接口介绍 1.1.Executor public interface Executor { void execute(Runnable command); } 执行提交的Runnable ...

随机推荐

  1. jconsole监控远程linux tomcat运行情况的配置 (转)

    来自:http://zhumeng8337797.blog.163.com/blog/static/100768914201242494649455/ 步骤如下: 1.编辑tomcat/bin/cat ...

  2. 使用Oracle Data Integrator Studio创建资料档案库

    一.Creating the Database Schema /*第1步:创建临时表空间 */ create temporary tablespace user_temp tempfile 'C:\a ...

  3. java 八种基本数据类型之与对应的封装类之间的相互转化

      迁移时间--2017年5月26日17:47:37 Author:Marydon 一.java数据类型之基本数据类型 UpdateTime--2017年1月9日17:31:14 (三)格式转换 1. ...

  4. Jquery的html方法里包含特殊字符的处理

    jqueryhtml()特殊字符 在使用jquery的html()方法时,有时候里面添加的html代码含有一些特殊字符,需要进行转义. 如下例子: inst_html = "<a st ...

  5. redmine3.3基于bitnami集成快速安装

    形而上学,此景此情,古今一般同!好的东西,逢人喜!redmine在当下还是不错的. 源码安装redmine,相对复杂基于ruby运行的!化繁为简实!追究极致万物! 00.环境及软件说明 bitnami ...

  6. ios中layoutsubview何时被调用

    layoutsubview和viewDidlayoutsubview(控制器)被调用的集中情况 一:当view的frame或bounds发生改变 1:直接改view的frame或bounds 会调用v ...

  7. SSD 为什么顺序写比随机写性能更好?

    SSD以Page为单位做读写,以Block为单位做垃圾回收,Page一般有16KB大小,Block一般有几十MB大小,SSD写数据的逻辑是: 1)将该块数据所在的Page读出 2)修改该Page中该块 ...

  8. Netty-gRPC介绍和使用

    转自:http://www.saily.top/2017/07/23/netty5/ gRPC Define your service using Protocol Buffers, a powerf ...

  9. 【LeetCode】199. Binary Tree Right Side View

    Binary Tree Right Side View Given a binary tree, imagine yourself standing on the right side of it, ...

  10. 怎样看待IT界业务,技术,管理的各自比重

    怎样看待IT界业务,技术,管理的各自比重   技术是根本,业务是个人能力的体现,管理一般随意,追求简单,眼光向IBM等有优秀管理经验的大公司看齐   重点从个人的喜好.性格方面来考虑分配比重,可以加上 ...