GNU Trove 2.0.3出现除零异常 求排查原因及触发场景
看起来你在使用GNU Trove 2.0.3时遇到了挺头疼的除零异常问题——而且更奇怪的是,栈轨迹指向的代码行根本没有除法操作,对吧?这背后其实有两个核心原因:栈轨迹行号不匹配,以及Trove内部特定场景下的逻辑触发了除零。
首先,为什么栈轨迹的行号不对?
你看到的异常行号和源码对应不上,大概率是因为运行时的字节码和你参考的源码行号映射出了问题:
- 你用的Trove jar包和查看的2.0.3源码虽然版本号一致,但编译时可能去掉了调试符号,或者源码被修改过重编译,导致行号错位;
- 你的项目类路径里混入了其他版本的Trove类,类加载器加载了错误版本的代码;
- 代码被混淆工具处理过,破坏了行号和源码的对应关系。
然后,Trove里哪些场景真的会触发除零异常?
抛开行号问题,Trove的哈希表实现中确实存在会触发除零的逻辑,主要集中在哈希表容量相关的操作上:
哈希表初始容量被设为0
Trove的哈希表(比如TPrimitiveHash及其子类)靠内部的_states数组跟踪元素状态,数组长度就是哈希表的容量。当容量为0时,计算元素索引的核心逻辑(比如hash % 容量)就会直接抛出除零异常。
可能的触发情况:比如显式调用了new TIntLongHashMap(0)这类构造函数(旧版本Trove可能没做最小容量校验),或者初始化时的逻辑bug导致_states数组被错误创建为长度0。扩容(rehash)时计算新容量出错
当哈希表元素数量达到阈值时,Trove会自动扩容。如果扩容逻辑计算新容量时出问题(比如旧容量为0时,计算后的新容量还是0),替换_states数组后,后续的put、get操作都会触发除零异常。比如TPrimitiveHash.rehash()方法中,若新容量计算逻辑有bug,就会出现这种情况。并发修改破坏哈希表内部状态
你的代码片段里用迭代器遍历test1Map并把元素放入testMap,如果是在多线程环境下执行,同时有其他线程修改这两个哈希表的状态(比如扩容、clear操作),很可能会导致哈希表的容量变量(比如_capacity)或_states数组被意外改成0,进而触发异常。
另外,你用的遍历方式for (int i = test1Map.size(); i-- > 0; ) { iter.advance(); }也有隐患:如果遍历过程中test1Map的size被其他线程修改,会导致advance()调用次数超过实际元素数,可能破坏迭代器或哈希表的内部状态。
给你的排查建议
- 确认运行时用的Trove jar包和你看的源码完全一致,检查类路径里有没有其他版本的Trove类;
- 排查代码里有没有显式传0作为哈希表初始容量的情况;
- 检查是否有多线程并发修改同一个Trove哈希表的场景,要么确保操作在单线程下执行,要么用线程安全的Trove变体;
- 考虑升级到Trove 3.x版本,2.0.3是比较老的版本,这类bug可能已经被修复了。
内容的提问来源于stack exchange,提问作者user6060190

