调用C++ Native方法时JVM崩溃,求排查解决方案
排查与修复JNI调用导致的JVM崩溃问题
核心问题分析
从错误信息EXCEPTION_ACCESS_VIOLATION(0xc00000005)和~BufferBlob::jni_fast_GetCharField来看,崩溃是JNI层访问空指针导致的,且断点未命中needTransform第一行,说明问题大概率出在Native方法注册阶段或参数传递前的非法内存访问。以下是针对性的排查与修复步骤:
1. 修正JNIEnv的非法强制转换
问题点
你直接将标准JNIEnv*强制转换为自定义的CustomJEnv*:
auto* tools = (CustomJEnv*)env;
如果CustomJEnv不是JNIEnv的合法子类,或GetEnv返回的是未初始化的无效指针,会直接触发内存访问错误,且崩溃发生在进入needTransform方法之前,导致断点无法命中。
修复
替换自定义CustomJEnv的使用,改用标准JNI API处理字符串转换:
// 新增字符串转换工具函数 std::string jniStringToStd(JNIEnv* env, jstring jstr) { if (jstr == nullptr) return ""; const char* utfChars = env->GetStringUTFChars(jstr, nullptr); std::string result(utfChars); env->ReleaseStringUTFChars(jstr, utfChars); return result; } jboolean cppFunc::needTransform(JNIEnv *env, jobject instance, jstring classloader, jstring classname){ std::string loaderStr = jniStringToStd(env, classloader); std::string classStr = jniStringToStd(env, classname); cppFunc::lastClass = loaderStr + "::" + classStr; return transformingClasses.count(cppFunc::lastClass) ? JNI_TRUE : JNI_FALSE; }
2. 处理ClassLoader为null的边界情况
问题点
transform方法中的loader参数可能为null(Bootstrap类加载器加载的类),此时调用loader.getName()会抛出Java层空指针异常,若该异常被JNI层错误处理,或传递null的jstring到Native方法后未做检查,会触发JNI层空指针访问。
修复
在Java层提前处理loader为空的情况:
@Override public byte[] transform(ClassLoader loader, String className, Class<?> classBeingRedefined, ProtectionDomain protectionDomain, byte[] classfileBuffer){ String loaderName = loader == null ? "bootstrap" : loader.getName(); if(nativeContent.INSTANCE.needTransform(loaderName, className)){ System.out.println("[BML][INFO] Transforming " + loaderName + "::" + className); // ... 后续逻辑 } return classfileBuffer; }
3. 验证JNI方法注册的正确性
问题点
若JNI方法签名、类路径或方法名不匹配,JVM会无法找到正确的Native方法实现,调用时会触发崩溃。
修复
- 使用
javah工具生成标准JNI头文件,对比现有签名:javah -classpath [你的类编译路径] gwy.bml.agent.nativeContent$cppFunc - 修正
JNI_OnLoad中类路径和注册逻辑,确保使用标准JNI API获取环境:JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *javaVm, void *reserved){ jvm = javaVm; JNIEnv* env = nullptr; jint envResult = javaVm->GetEnv((void**)&env, JNI_VERSION_9); if (envResult != JNI_OK) return JNI_ERR; jclass targetClass = env->FindClass("gwy/bml/agent/nativeContent$cppFunc"); if (targetClass == nullptr) return JNI_ERR; jint regResult = env->RegisterNatives(targetClass, methods, sizeof(methods)/sizeof(JNINativeMethod)); if (regResult != JNI_OK) return JNI_ERR; // 后续文件操作逻辑... return JNI_VERSION_9; }
4. 确保DLL加载顺序与依赖完整性
问题点
若依赖DLL(如python311.dll)版本不匹配或加载失败,BML_T.dll会出现未定义行为,导致JVM崩溃。
修复
在Java静态代码块中增加加载日志与错误捕获:
static { for (String dll: dlls) { File dllFile = new File(dll); try { if(!dllFile.exists()) Files.copy(Objects.requireNonNull(agentMain.class.getResourceAsStream("/" + dll)), Path.of("./" + dll)); System.load(dllFile.getAbsolutePath()); System.out.println("[BML][INFO] 成功加载DLL: " + dll); } catch (IOException | UnsatisfiedLinkError e) { System.err.println("[BML][ERROR] 加载DLL失败: " + dll + ", 错误信息: " + e.getLocalizedMessage()); e.printStackTrace(); throw new RuntimeException("无法加载必要的DLL", e); } } }
内容的提问来源于stack exchange,提问作者Nahida
相关产品推荐
相关产品推荐

