- 做除法的时候除数为 0;
- 用户输入年龄时输入了一个负数;
- 用 new 运算符动态分配空间时,空间不够导致无法分配;
- 访问数组元素时,下标越界;打开文件读取时,文件不存在。
NDK异常信息一般有三个要素:
- 信号
- 调用栈信息
- 寄存器信息
- 错误信号:11是信号量sigNum,SIGSEGV是信号的名字,SEGV_MAPERR是SIGSEGV下的一种类型。
- 寄存器快照:进程收到错误信号时保存下来的寄存器快照,其中PC寄存器存储的就是下个要运行的指令(出错的位置)。
- 调用栈:#00是栈顶,#02是栈底,#02调用#01调用#00方法,#00的方法时libspirit.so中的Spirit类下的testCrash方法,出错的地方是testCrash方法内汇编偏移17(不是行号哦!)
通常的来源有三个:
1、硬件发生异常,即硬件(通常是CPU)检测到一个错误条件并通知Linux内核,内核处理该异常,给相应的进程发送信号。硬件异常的例子包括执行一条异常的机器语言指令,诸如,被0除,或者引用了无法访问的内存区域。大部分信号如果没有被进程处理,默认的操作就是杀死进程。在本文中,SIGSEGV(段错误),SIGBUS(内存访问错误),SIGFPE(算数异常)属于这种信号。
2、进程调用的库发现错误,给自己发送中止信号,默认情况下,该信号会终止进程。在本文中,SIGABRT(中止进程)属于这种信号。
3、用户(手贱)或第三方App(恶意)通过kill-信号 pid的方式给错误进程发送,这时signal中的si_code会小于0。
能够捕获任何异常的 catch 语句
catch(...) {
...
}
try {
in.substract_mean_normalize(mean_vals, norm_vals);
} catch (...) {
return -1;
}
一. 空指针:
说明:
- 指针引用不能访问地址
- 指针为空
空指针是很容易出现的一种bug,但是它也很容易被发现和修复。比如以下代码就会报空指针:
- 操作不能访问地址:
解决方案:使用前加非空判断
二. 野指针:
说明:指针指向无效地址:
- 如果该地址是不可读不可写,那么立马会遇到crash(内核给进程发送错误信息SIGSEGV)
- 如果该指针的地址可写,那么可能等一会才会出现崩溃(其他指针修改了这一块地址),这时候查看调用栈和野指针所在代码部分可能根本没有关联。
解决方案:
- 指针变量一定要初始化,特别是结构体或类中的成员变量的指针
- 使用完后,在不用的情况,尽量执为NULL(如果别的地方也有指针指向这段内存就不好解决)
Bug评述
野指针的bug,特别是内存破坏的问题,有时候查起来毫无头绪,没有一点线索,让开发者感觉到很茫然和无助( Bugly上报的堆栈看不出任何问题)。可以说内存破坏bug是服务器稳定性最大的杀手,也是C/C++在开发应用方面相比于其它语言(如Java, C#)的最大劣势之一。
四. 内存泄漏
说明:
c与c++没有像java那样自动回收的GC机制,所以使用完需要手动释放,如果没有释放,就造成内存泄漏。
比如:
解决方案:
- 用完后进行释放
例如:
1、内存溢出
内存溢出是指程序在申请内存时没有足够的内存空间供其使用。原因可能如下:
(1)内存中加载的数据过于庞大;
(2)代码中存在死循环;
(3)递归调用太深,导致堆栈溢出等;
(4)内存泄漏最终导致内存溢出;
2、内存泄漏
内存泄漏是指使用new申请内存, 但是使用完后没有使用delete释放内存,导致占用了有效内存。
六. 格式化参数错误
- 需要格式化参数类型的时候,有可能出现错误
比如
- 在书写输出格式和参数时,要做到参数个数和类型都要与输出格式一致。
- 在GCC的编译选项中加入-wformat,让GCC在编译时检测出此类错误。
六. 除以0
分母为0的这种情况,会很快Crash,一般都是在实际运行环境中还有可能出现,所以编码习惯的时候应该尽量习惯性去判断下
示例代码
/**
* 除以0
*/
void zeroDiv() {
int a = 1;
int b = a / 0; //整数除以0,产生SIGFPE信号,导致Crash
LOGE("b:%d", b);
}
解决方案
- 在有除法的时候,判断下分母为0的情况
{
try{
__android_log_print(ANDROID_LOG_DEBUG, "NDK-peng", "*****222*error*******error****************************",2);
int *p=NULL;
*p=1;
}catch(Exception){
__android_log_print(ANDROID_LOG_DEBUG, "NDK-peng", "**111111****error*******error****************************",2);
return 9999;
}
__android_log_print(ANDROID_LOG_DEBUG, "NDK-peng", "*****222*error*******error****************************",2);
int *p=NULL;
__android_log_print(ANDROID_LOG_DEBUG, "NDK-peng", "*****444444444***************************",2);
*p=1;
if(env->ExceptionCheck()) {
__android_log_print(ANDROID_LOG_DEBUG, "NDK-peng", "*****33333333333*error*******error****************************",2);
env->ExceptionDescribe(); // writes to logcat
env->ExceptionClear();
// return 9999999;
}else{
__android_log_print(ANDROID_LOG_DEBUG, "NDK-peng", "正常",2);
// return 555;
}
char* a = NULL;
int val1 = a[1] - '0';
LOGE("JNI, process code %d, cnt %d", m, n);
int *p=NULL;
*p=1;
int a=1;
int b=0;
int yyyy=a/b;
int a=1;
int b=0;
int yyyy=a/0;
#include <jni.h>
#include <string>
extern "C" JNIEXPORT jstring JNICALL
Java_sport_yuedong_com_myapplication_MainActivity_stringFromJNI(
JNIEnv *env,
jobject /* this */) {
std::string hello = "333333";
int a = 1;
int b = 0;
int yyyy = a / b;
try {
} catch (...) {
}
return env->NewStringUTF(hello.c_str());
}
extern "C" JNIEXPORT jstring JNICALL
Java_sport_yuedong_com_myapplication_MainActivity_stringFromJNI(
JNIEnv *env,
jobject /* this */) {
std::string hello = "333333";
try {
} catch (...) {
}
char* a = NULL;
int val1 = a[1] - '0';
if(env->ExceptionCheck()) {
env->ExceptionDescribe(); // writes to logcat
env->ExceptionClear();
}
extern "C"
JNIEXPORT jstring JNICALL
Java_sport_yuedong_com_myapplication_MyException_stringFromJNI(JNIEnv *env, jobject jobjectOther) {
std::string hello = "22";
std::string error = "error";
jclass jclass = env->FindClass("sport/yuedong/com/myapplication/MyException");
jmethodID mid = env->GetMethodID(jclass, "operation", "()I");
jmethodID mid2 = env->GetMethodID(jclass, "<init>", "()V");
jobject jobject1 = env->NewObject(jclass, mid2);
env->CallIntMethod(jobject1, mid);
jthrowable jthrowable1 = env->ExceptionOccurred();
//c调用java的处理,可以做异常检查
if (jthrowable1) {
env->ExceptionDescribe();
env->ExceptionClear();
return env->NewStringUTF(error.c_str());
}
return env->NewStringUTF(hello.c_str());
}
异常检查:异常检查只有c调用java的时候才有用。JNI中抛异常很经典:找异常类,调用ThrowNew抛出之;所以,可以写一个工具函数。
1.解决java.lang.StackOverflowError: stack size 8MB报错问题:
<span style="color:#000000"><span style="color:#cccccc"><code class="language-bash">export NDK_HOME=/Users/mac/Library/Android/sdk/ndk/16.1.4479499/ndk-build
export PATH=$PATH:$NDK_HOME/</code></span></span>
2.理解StackOverflowError与OutOfMemoryError
- 如果你确认递归实现没有问题,你可以通过-Xss参数增加栈的大小,这个参数可以在项目配置或命令行指定。
ndk {
abiFilters "armeabi"
}
4.奔溃,因为找不到方法或者so库奔溃,方法找不到原因:要包名一样才行的