- 做除法的時候除數爲 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庫奔潰,方法找不到原因:要包名一樣纔行的