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

在JNI集成的C++应用中,如何符号化Java代码的ASan栈追踪?

JNI嵌入JVM场景下ASan栈追踪的Java代码符号化问题

问题背景

我维护一个混合C和Java代码的应用,通过JNI在C中嵌入JVM。启用ASan(具体使用LSan)检测内存泄漏时,发现Java代码对应的栈追踪完全没有符号信息,还出现了<unknown module>标记。我有两个疑问:

  1. 是否有办法对这些无符号的地址进行符号化?
  2. 这些<unknown module>是什么,如何对其进行符号化?

示例栈追踪1

Direct leak of 8 byte(s) in 1 object(s) allocated from:
#0 0x4be0ed in malloc (/path/to/my/binary+0x4be0ed)
#1 0x7f047a6ebd2c  (/usr/lib/jvm/java-8-openjdk-amd64/jre/lib/amd64/server/libjvm.so+0x958d2c)
#2 0x7f047a3a3cee  (/usr/lib/jvm/java-8-openjdk-amd64/jre/lib/amd64/server/libjvm.so+0x610cee)
#3 0x7f047a695f8d  (/usr/lib/jvm/java-8-openjdk-amd64/jre/lib/amd64/server/libjvm.so+0x902f8d)
#4 0x7f047a3e44ff  (/usr/lib/jvm/java-8-openjdk-amd64/jre/lib/amd64/server/libjvm.so+0x6514ff)
#5 0x7f047a690c82  (/usr/lib/jvm/java-8-openjdk-amd64/jre/lib/amd64/server/libjvm.so+0x8fdc82)
#6 0x7f047a693137  (/usr/lib/jvm/java-8-openjdk-amd64/jre/lib/amd64/server/libjvm.so+0x900137)
#7 0x7f047a6933ec  (/usr/lib/jvm/java-8-openjdk-amd64/jre/lib/amd64/server/libjvm.so+0x9003ec)
#8 0x7f0466ad17a6  (<unknown module>)
#9 0x7f0466ac0e3f  (<unknown module>)
#10 0x7f0466ac0e3f  (<unknown module>)
#11 0x7f0466ac0e3f  (<unknown module>)
#12 0x7f0466ac0e3f  (<unknown module>)
#13 0x7f0466ac0e3f  (<unknown module>)
#14 0x7f0466ac0e3f  (<unknown module>)
#15 0x7f0466ac0e3f  (<unknown module>)
#16 0x7f0466ac0e3f  (<unknown module>)
#17 0x7f0466ac0e3f  (<unknown module>)
#18 0x7f0466ac0e3f  (<unknown module>)
#19 0x7f0466ab94e6  (<unknown module>)
#20 0x7f047a42a48f  (/usr/lib/jvm/java-8-openjdk-amd64/jre/lib/amd64/server/libjvm.so+0x69748f)
#21 0x7f047a428b8e  (/usr/lib/jvm/java-8-openjdk-amd64/jre/lib/amd64/server/libjvm.so+0x695b8e)
#22 0x7f047a82bc3e  (/usr/lib/jvm/java-8-openjdk-amd64/jre/lib/amd64/server/libjvm.so+0xa98c3e)
#23 0x7f047a5ae365  (/usr/lib/jvm/java-8-openjdk-amd64/jre/lib/amd64/server/libjvm.so+0x81b365)
#24 0x7f047a5b5706  (/usr/lib/jvm/java-8-openjdk-amd64/jre/lib/amd64/server/libjvm.so+0x822706)
#25 0x7f047a5b6e24  (/usr/lib/jvm/java-8-openjdk-amd64/jre/lib/amd64/server/libjvm.so+0x823e24)
#26 0x7f047a41dbd0  (/usr/lib/jvm/java-8-openjdk-amd64/jre/lib/amd64/server/libjvm.so+0x68abd0)
#27 0x7f0466ae2a87  (<unknown module>)
#28 0x7f0466ac10f5  (<unknown module>)
#29 0x7f0466ac0e3f  (<unknown module>)

示例栈追踪2

Direct leak of 24 byte(s) in 1 object(s) allocated from:
#0 0x4be0ed in malloc (/path/to/my/binary+0x4be0ed)
#1 0x7f047a6ebd2c  (/usr/lib/jvm/java-8-openjdk-amd64/jre/lib/amd64/server/libjvm.so+0x958d2c)
#2 0x7f047a1d4538  (/usr/lib/jvm/java-8-openjdk-amd64/jre/lib/amd64/server/libjvm.so+0x441538)
#3 0x7f047a1d45f4  (/usr/lib/jvm/java-8-openjdk-amd64/jre/lib/amd64/server/libjvm.so+0x4415f4)
#4 0x7f047a3e4e58  (/usr/lib/jvm/java-8-openjdk-amd64/jre/lib/amd64/server/libjvm.so+0x651e58)
#5 0x7f047a3e4eb6  (/usr/lib/jvm/java-8-openjdk-amd64/jre/lib/amd64/server/libjvm.so+0x651eb6)
#6 0x7f047a49ceb9  (/usr/lib/jvm/java-8-openjdk-amd64/jre/lib/amd64/server/libjvm.so+0x709eb9)
#7 0x7f0475cd6e8c in Java_java_lang_System_setIn0 (/usr/lib/jvm/java-8-openjdk-amd64/jre/lib/amd64/libjava.so+0x14e8c)
#8 0x7f0466ad17a6  (<unknown module>)
#9 0x7f0466ac10f5  (<unknown module>)
#10 0x7f0466ab94e6  (<unknown module>)
#11 0x7f047a42a48f  (/usr/lib/jvm/java-8-openjdk-amd64/jre/lib/amd64/server/libjvm.so+0x69748f)
#12 0x7f047a428b8e  (/usr/lib/jvm/java-8-openjdk-amd64/jre/lib/amd64/server/libjvm.so+0x695b8e)
#13 0x7f047a429187  (/usr/lib/jvm/java-8-openjdk-amd64/jre/lib/amd64/server/libjvm.so+0x696187)
#14 0x7f047a864f9a  (/usr/lib/jvm/java-8-openjdk-amd64/jre/lib/amd64/server/libjvm.so+0xad1f9a)
#15 0x7f047a4ab81e in JNI_CreateJavaVM (/usr/lib/jvm/java-8-openjdk-amd64/jre/lib/amd64/server/libjvm.so+0x71881e)
#16 0x71c1ab in jni::JVMManager::Start() /path/.../jni/jvm_manager.cpp:138:17
#17 0x4f3fbe in (anonymous namespace)::maybe_setup_jvm(...) /path/to/my/main/file.cpp:48:30
#18 0x4f3fbe in main /path/to/my/main/file.cpp:158:22
#19 0x7f04798c7d09 in __libc_start_main csu/../csu/libc-start.c:308:16

解答

1. 如何对Java代码对应的栈地址进行符号化

ASan原生仅支持解析C++这类原生代码的符号,Java代码运行在JVM的字节码解释器或JIT编译层,需要结合JVM专属工具链来解析:

  • 安装带调试符号的JDK:
    对于OpenJDK,安装对应的调试包(如openjdk-8-dbg),确保JVM本身的符号完整,同时为Java代码保留调试信息。
  • 启用JVM栈帧指针保留:
    启动应用时添加参数-XX:+PreserveFramePointer,让JVM保留栈帧指针,帮助工具更精准捕获Java栈信息。
  • 结合JVM工具解析地址:
    当ASan输出泄漏地址后,用jstack导出目标进程的当前栈,或者查看应用崩溃时生成的hs_err_pid文件,里面会包含Java栈与地址的映射,可关联到具体的Java方法和行号。
  • 使用Java专用分析工具:
    用async-profiler这类工具,它能同时捕获原生和Java栈的完整信息,配合ASan的泄漏地址,直接定位到Java代码中的泄漏点。

2. <unknown module>的本质与符号化方法

这些标记对应的是JVM运行时JIT编译生成的Java代码段,或是动态生成的字节码(比如动态代理、Lambda表达式)。由于JIT代码是运行时动态生成的,没有被系统动态链接器注册为标准模块,所以ASan无法识别。

符号化方法:

  • 启用JIT编译的符号输出:
    添加JVM参数-XX:+UnlockDiagnosticVMOptions -XX:+PrintAssembly -XX:+DebugNonSafepoints,JVM会在JIT编译时输出代码地址与Java方法的映射关系到控制台或日志,你可以根据ASan给出的地址在日志中查找对应方法。
  • 用JVM工具查看JIT代码缓存:
    执行jcmd <进程ID> Compiler.codecache,查看JIT代码缓存中所有编译方法的地址范围,匹配ASan输出的地址即可找到对应的Java方法。
  • 加载libjsig.so辅助识别:
    设置环境变量LD_PRELOAD=/usr/lib/jvm/java-8-openjdk-amd64/jre/lib/amd64/libjsig.so,这个库能让ASan等原生工具识别JIT生成的代码模块,直接解析出对应的Java方法。

内容的提问来源于stack exchange,提问作者vpshastry

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.29 09:57:10