minSDK≥22调用free()触发内存错误,minSDK=21运行正常
Android API 22+ JNI内存分配差异与free报错修复
核心原因
API 22及以上的Android系统,JNI层内存管理有两处关键变化,直接触发你的free错误:
- 字符串内存所有权规则变更
API21及以下,GetStringUTFChars返回的指针可能指向libc malloc分配的堆内存,直接free不会报错;但API22+开始,为了性能优化,该函数大概率返回JVM内部共享内存的指针,这类内存禁止用free释放,必须通过ReleaseStringUTFChars归还给JVM。 - 内存分配器的严格性提升
API22起,bionic libc的malloc/free实现新增了严格的非法指针检测机制。如果代码存在重复释放、释放未分配指针,或者跨模块分配/释放内存(比如JNI用malloc分配,Java层间接释放),API21时可能静默忽略,但API22+会直接抛出Invalid address...错误。
修复步骤
- 修正字符串内存释放方式:如果
filePaths[i]是通过GetStringUTFChars获取的C字符串指针,立刻替换free(filePaths[i])为(*env)->ReleaseStringUTFChars(env, javaStringObj, filePaths[i])(其中javaStringObj是对应的Java字符串对象)。 - 确保分配与释放匹配:如果
filePaths[i]是你手动用malloc/calloc分配的内存,必须用同libc的free释放,禁止混用JNI的内存释放函数;如果是通过JNIEnv分配的内存(比如NewByteArray),必须用对应的Release函数。 - 排查野指针/重复释放:添加日志打印,确认每个指针在释放前的状态,避免重复释放或释放未初始化的非法地址。
代码示例修正
假设原错误代码片段:
// 假设filePaths是存储Java字符串的数组 jstring* filePaths = (jstring*)malloc(sizeof(jstring) * fileCount); // ... 填充filePaths逻辑 for (int i = 0; i < fileCount; i++) { const char* cPath = (*env)->GetStringUTFChars(env, filePaths[i], NULL); // ... 使用cPath做业务逻辑 free((void*)cPath); // API22+此处报错 } free(filePaths);
修正后代码:
jstring* filePaths = (jstring*)malloc(sizeof(jstring) * fileCount); // ... 填充filePaths逻辑 for (int i = 0; i < fileCount; i++) { const char* cPath = (*env)->GetStringUTFChars(env, filePaths[i], NULL); // ... 使用cPath做业务逻辑 (*env)->ReleaseStringUTFChars(env, filePaths[i], cPath); // 正确释放JNI字符串内存 } free(filePaths); // 手动malloc的数组,用free释放合法
内容的提问来源于stack exchange,提问作者nima
相关产品推荐
相关产品推荐

