最近给服务器提供协议编解码库,出现较多内存相关的问题,做个记录,顺便给有相同需求的同学提供参考!
1、C/C++层创建对象,返回指针给Java层,Java层使用该指针作为后续操作的参数,在windows 64位系统中,出现地址无法访问的问题:
参考:https://www.jianshu.com/p/144136be6be9
在java与c构建多实例的案例中,java中对象保存一个long型成员变量,作为对应c实例的指针,jni层在新建c对象后,将对象指针(64位)转成长整型返回给java成员变量保存。然而在某些情况下,调用c实例的方法会出现ACCESS_VIOLATION,并且在windows上会报错,但在linux上就没问题。经过排查,问题最终定位如下:
在jni.h中,关于jlong(java long)的typedef并不是唯一的,在windows上,long总是32位(所以在linux上没问题),所以在jni层将地址转长整型的操作,必须要用__int64(long, long int, long long 都不行)保存变量,并返回给java。
JNIEXPORT jlong JNICALL Java_com_xdja_test_TestJNIUtil_getNativePointer(JNIEnv *env, jobject obj){
char *key_list = (char *)malloc(200);
if (key_list == NULL) {
LOGD("key_list == null");
return 0;
}
memset(key_list, 0, 200);
printf("getNativePointer: obj:%08x, key_list:%08x,\r\n", obj, key_list);
//return __int64(key_list); //windows需要返回的是这个地址
//
return jlong(key_list);
}
2、本地类对象方法和实例对象方法的声明不同:
//在创建支持的C/C++程序中添加两个方法,分别是非静态和静态,可以使用对应java类的类方法或者实例方法调用这些接口;
public native String stringFromJNI1();
public static native String stringFromJNI2();
extern "C"
JNIEXPORT jstring JNICALL
Java_com_xdja_test_TestJNIUtil_stringFromJNI1(JNIEnv *env, jobject instance) {
// TODO
return env->NewStringUTF(returnValue);
}
extern "C"
JNIEXPORT jstring JNICALL
Java_com_xdja_test_TestJNIUtil_stringFromJNI2(JNIEnv *env, jclass type) {
// TODO
return env->NewStringUTF(returnValue);
}
参考:https://blog.csdn.net/cloverjf/article/details/78654366?spm=1001.2014.3001.5501
3、Crash调试手段:
Android的NDK调试工具ndk-stack,ndk-stack是ndk开发工具包下提供的好用工具,能结合崩溃日志给出详细分析;
基础用法: ndk-stack -sym 带有符号表的so所在的目录 -dump 崩溃日志:
比如 ~/Library/Android/sdk/ndk/android-ndk-r16b/ndk-stack -sym app/build/intermediates/cmake/debug/obj/arm64-v8a -dump crash.log
参考:https://blog.csdn.net/ReadyShowShow/article/details/109095211
如果是给服务器或者Java应用程序使用的JNI库,调试Crash日志的方法,包括常规手段:日志法、return或者注释代码;
如果能从Core-dump中恢复出调用栈,那么调试效率就会高很多了!
补记:20210702
事出蹊跷必有因,在Linux平台运行正常的代码,在Windows平台就会出现异常,原来是Windows平台的calloc申请出来的内存并不会自动清0,导致结构体中指针对象为非法指针,内存释放就出现了野指针操作,崩溃也就不奇怪了!话说不能在同一个地方摔倒两次,但同时在这个地方卡了几天,真是惭愧!
visual studio 2017 中文注释影响代码逻辑,比方:
// 中文注释
if(xxx) // 这一行被当作是注释了
{
do_something...
}
switch(value){
case 0:
break;
// 中文注释
case 1: // 这一行被当作是注释了,导致case分支判断失败,走到了default分支
break;
default:
break;
}
C代码最好是能做一次PC-lint检查内存相关问题,保证代码质量!
-------------------广告线---------------
项目、合作,欢迎勾搭,邮箱:promall@qq.com
本文为呱牛笔记原创文章,转载无需和我联系,但请注明来自呱牛笔记 ,it3q.com