JVM阻塞无有效线程信息排查求助:JNI调用后进程挂起
编码转换Jar包进程阻塞无响应问题分析求助
问题现象
- 通过
java -jar运行编码转换Jar包,程序偶尔出现阻塞无响应,且无法正常退出 - 常规
jstack <PID>无法获取线程信息,必须使用jstack -F <PID>才能输出日志,但无法直接定位JVM当前行为
jstack -F 输出日志
Debugger attached successfully.
Server compiler detected.
JVM version is 25.144-b01
Deadlock Detection:No deadocks found.
Thread 18532: (state=IN_VM)
Thread 18531:(state=BLOCKED)
- java.lang.object.wait(long) @bci=0 (Interpreted frame)
- java.lang.ref.ReferenceQueue.remove(long) @bci=59, line=143 (Interpreted frame)
- java.lang.ref.ReferenceQueue.remove() @bci=2, line=164 (Interpreted frame)
- java.lang.ref.Finalizer$FinalizerThread.run()@bci=36, line=209 (Interpreted frame)
Thread 18530: (state = BLOCKED)
- java.lang.object.wait(long) @bci=0(Interpreted frame)
- java.lang.object.wait() @bci=2,line=502 (Interpreted frame)
- java.lang.ref.Reference.tryHandlePending(boolean) @bci=54, line=191 (Interpreted frame)
- java.lang.ref.Reference$ReferenceHandler.run() @bci=1, line=153 (Interpreted frame)
Thread 18518: (state=IN_VM)
核心代码
主入口类
public class UTF8TOGB18030 { public static void main(String[] args) throws Exception { if (args == null || args.length < 1) { throw new RuntimeException("please set file name"); } String input = args[0].trim(); if ("".equals(input)) { throw new RuntimeException("input is blank."); } FileConverter converter = new FileConverter("UTF-8", "GB18030", input, input); converter.convert(); } }
工具类(依赖第三方JNI库JniUtils,无源码)
public class FileConverter { private String inputEncoding; private String outputEncoding; private String inputFilePath; private String outputFilePath; private Logger log = LoggerFactory.getLogger(FileConverter.class); public FileConverter(String inputEncoding, String outputEncoding, String inputFilePath, String outputFilePath) { this.inputEncoding = inputEncoding; this.outputEncoding = outputEncoding; this.inputFilePath = inputFilePath; this.outputFilePath = outputFilePath; } public void convert() throws Exception { // 创建临时文件 File tempOutputFile = new File(outputFilePath + "_" + new SimpleDateFormat("yyyyMMddHHmmssSSS").format(new Date())); String logInfo = "convert begin, input file " + inputFilePath; log.info(logInfo); logInfo = "convert begin, output file " + tempOutputFile.getCanonicalPath(); log.info(logInfo); String ccOpts = inputR + "2" + outputX + "2"; ccOpts = ccOpts + "??QQNUYYNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNN"; // 调用第三方JNI库执行转换 int rsInt = JniUtils.txtFileConv( inputEncoding, inputFilePath, 65536, outputEncoding, tempOutputFile.getCanonicalPath(), 65536, ccOpts, "3B3NEI", "001", "filescheduler", "./err.log", 0); if (rsInt != 0) { if (tempOutputFile.exists()) { logInfo = "convert error, delete tempOutputFile " + tempOutputFile.getCanonicalPath(); log.info(logInfo); tempOutputFile.delete(); throw new RuntimeException("convert error, return code is " + rsInt); } } tempOutputFile.renameTo(new File(outputFilePath)); } }
(注:修复了原文代码中的拼写错误,如logqInfo→logInfo、rsint→rsInt,补充了缺失的成员变量定义)
补充诊断信息
jstack -F -m输出片段:
-------------------1111------------------------------- 0x00007f43a0e0665b _ZN14AttachListenerl5is_init_triggerEv + 0x4b 0x00007f43a140768d _ZL19signal thread entryPl0JavaThreadP6Thread + 0x32d 0x00007f43a1560753 _ZN10JavaThread17thread_main_innerEv + 0x103 0x00007f43a156089c _ZN10JavaThread3runEy + 0x11c 0x00007f43a1411338 _ZL10java_startP6Thread + 0x108
- 另一次进程挂起(PID=1079)的诊断结果:
top -H -p 1079显示两个线程CPU占用达99.5%,jstack仅标记其状态为IN_VMpstack 1079显示:一个线程阻塞于pthread_create@GLIBC_2.2.5,另一个阻塞于AttachListener::is_init_trigger()
问题分析
从现有诊断信息来看,问题根源大概率出在第三方JNI库JniUtils的本地代码实现:
- 程序阻塞发生在调用
JniUtils.txtFileConv()之后,该方法直接调用Linux本地SO库,无源码可见,无法排查内部逻辑 IN_VM状态表示线程正在执行JVM内部代码或本地代码,结合pstack结果,本地代码中出现了pthread_create阻塞和AttachListener相关死循环(高CPU占用),说明本地代码可能存在线程创建失败、资源竞争或死循环问题- 常规
jstack无法获取线程信息,是因为JVM被本地代码阻塞,无法响应信号,只能通过强制模式jstack -F获取快照
建议解决方向
- 联系第三方JNI库提供商,反馈本地代码导致的阻塞问题,请求修复或提供调试支持
- 尝试替换该JNI库,改用纯Java实现的编码转换工具(如
InputStreamReader/OutputStreamWriter结合文件流处理),规避本地代码风险 - 若必须使用该库,可添加超时机制:通过线程池异步执行转换任务,设置超时时间,超时则强制销毁线程并清理资源
内容的提问来源于stack exchange,提问作者sdcyst
相关产品推荐
相关产品推荐

