You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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,补充了缺失的成员变量定义)

补充诊断信息

  1. 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
  1. 另一次进程挂起(PID=1079)的诊断结果:
  • top -H -p 1079显示两个线程CPU占用达99.5%,jstack仅标记其状态为IN_VM
  • pstack 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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.16 11:22:06