前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >JNI 异常处理

JNI 异常处理

作者头像
Yif
发布2019-12-25 19:25:54
9280
发布2019-12-25 19:25:54
举报

野指针

野指针简介

野指针指向的是一个无效的地址。

  • 该地址如果不可读不可写,马上会Crash,内核给进程发送错误的信号SIGSEGV,这时候bug会很快被发现。
  • 如果放问地址可写,而且通过野指针修改了该处的内存,那么很有可能等一段时间才会发送Crash,这时候查看Crash调用的栈,和野指针所在的代码部分,几乎没有任何关联。

避免野指针崩溃的方法

  • 在指针变量定义时候,一定要初始化,特别是在结构体或者类中的成员指针变量
  • 在释放了指针指向的内存后,要把该指针置为NULL(如果在别处,也有指针指向该处的内存这种方式就不好解决)。
  • 想要解决野指针问题,不能靠看代码,使用代码分析工具,而要使用专业的内存检测工具才能发现bug。

异常

异常产生原因

  • 编译时异常:编译器在编译期间,对于受检异常必须进行try...catch或者throws进行处理,否则无法编译通过,比如File,stream,线程操作等;
  • 运行时异常:Java程序在运行期间所产生的异常,RuntimeException及其子类都统称为非受检查异常,例如:NullPointExecrption、NumberFormatException(字符串转换为数字)、ArrayIndexOutOfBoundsException(数组越界)、ClassCastException(类型转换错误)、ArithmeticException(算术错误)等;

Java与JNI处理异常区别

  • Java中可以用try...catch机制来捕获并处理异常;
  • 如果在Java中发生运行时异常,没有使用try...catch来捕获,会导致程序直接奔溃退出,后续的代码都不会被执行;
  • 编译时异常,是在方法声明时显示用throw声明了某一个异常,编译器要求在调用的时候必须显示捕获处理;
  • 而在JNI中,由于JNI没有像Java一样有try...catch...final这样的异常处理机制,面且在本地代码中调用某个JNI接口时如果发生了异常,后续的本地代码不会立即停止执行,而会继续往下执行后面的代码;

处理异常的方式

  • ExceptionCheck
  • ExceptionOccurred
ExceptionCheck

调用了JNI的ExceptionCheck函数检查最近一次JNI调用是否发生了异常,如果有异常这个函数返回JNI_TRUE,否则返回JNI_FALSE

//异常捕获 ,检查JNI调用是否有异常 if(env->ExceptionCheck()){ env->ExceptionDescribe(); env->ExceptionClear();//清除引发的异常,在Java层不会打印异常堆栈信息,如果不清除,后面的调用ThrowNew抛出的异常堆栈信息会 //覆盖前面的异常信息 jclass cls_exception = env->FindClass("java/lang/Exception"); env->ThrowNew(cls_exception,"call java static method ndk error"); return; }

123456789

//异常捕获 ,检查JNI调用是否有异常if(env->ExceptionCheck()){    env->ExceptionDescribe();    env->ExceptionClear();//清除引发的异常,在Java层不会打印异常堆栈信息,如果不清除,后面的调用ThrowNew抛出的异常堆栈信息会//覆盖前面的异常信息    jclass cls_exception = env->FindClass("java/lang/Exception");    env->ThrowNew(cls_exception,"call java static method ndk error");    return;}

ExceptionOccurred

异常检查JNI还提供了另外一个接口,ExceptionOccurred,如果检测有异常发生时,该函数会返回一个指向当前异常的引用。作用和ExceptionCheck一样,两者的区别在于返回值不一样。

//异常捕获,第二种方法 if(env->ExceptionOccurred()){ env->ExceptionDescribe(); env->ExceptionClear(); jclass cls_exception = env->FindClass("java/lang/Exception"); env->ThrowNew(cls_exception,"call java static method ndk error"); return; }

12345678

//异常捕获,第二种方法if(env->ExceptionOccurred()){    env->ExceptionDescribe();    env->ExceptionClear();    jclass cls_exception = env->FindClass("java/lang/Exception");    env->ThrowNew(cls_exception,"call java static method ndk error");    return;}

异常抛出工具类

void JNU_ThrowByName(JNIEnv *env, const char *name, const char *msg) { // 查找异常类 jclass cls = (*env)->FindClass(env, name); /* 如果这个异常类没有找到,VM会抛出一个NowClassDefFoundError异常 */ if (cls != NULL) { (*env)->ThrowNew(env, cls, msg); // 抛出指定名字的异常 } /* 释放局部引用 */ (*env)->DeleteLocalRef(env, cls); }

1234567891011

void JNU_ThrowByName(JNIEnv *env, const char *name, const char *msg) {     // 查找异常类     jclass cls = (*env)->FindClass(env, name);     /* 如果这个异常类没有找到,VM会抛出一个NowClassDefFoundError异常 */     if (cls != NULL) {         (*env)->ThrowNew(env, cls, msg); // 抛出指定名字的异常     }     /* 释放局部引用 */     (*env)->DeleteLocalRef(env, cls); }

异常发生后释放资源

在异常发生后,释放资源是一件很重要的事情。下面的例子中,调用 GetStringChars 函数后,如果后面的代码发生异常,要记得调用 ReleaseStringChars 释放资源。

JNIEXPORT void JNICALL Java_pkg_Cls_f(JNIEnv *env, jclass cls, jstring jstr) { const jchar *cstr = (*env)->GetStringChars(env, jstr); if (c_str == NULL) { return; } ... if ((*env)->ExceptionCheck(env)) { /* 异常检查 */ (*env)->ReleaseStringChars(env, jstr, cstr); // 发生异常后释放前面所分配的内存 return; } ... /* 正常返回 */ (*env)->ReleaseStringChars(env, jstr, cstr); }

123456789101112131415

JNIEXPORT void JNICALL Java_pkg_Cls_f(JNIEnv *env, jclass cls, jstring jstr) {     const jchar *cstr = (*env)->GetStringChars(env, jstr);     if (c_str == NULL) {         return;      }     ...     if ((*env)->ExceptionCheck(env)) { /* 异常检查 */         (*env)->ReleaseStringChars(env, jstr, cstr); // 发生异常后释放前面所分配的内存         return;      }     ...     /* 正常返回 */     (*env)->ReleaseStringChars(env, jstr, cstr);}

总结

  1. 当调用一个JNI函数后,必须先检查、处理、清除异常后再做其它 JNI 函数调用,否则会产生不可预知的结果;
  2. 一旦发生异常,立即返回,让调用者处理这个异常。或 调用 ExceptionClear 清除异常,然后执行自己的异常处理代码;
  3. 异常处理的JNI函数:
    • ExceptionCheck:检查是否发生了异常,若有异常返回JNI_TRUE,否则返回JNI_FALSE;
    • ExceptionOccurred:检查是否发生了异常,若用异常返回该异常的引用,否则返回NULL;
    • ExceptionDescribe:打印异常的堆栈信息;
    • ExceptionClear:清除异常堆栈信息;
    • ThrowNew:在当前线程触发一个异常,并自定义输出异常信息 jint (JNICALL *ThrowNew) (JNIEnv *env, jclass clazz, const char *msg);
    • Throw:丢弃一个现有的异常对象,在当前线程触发一个新的异常 jint (JNICALL *Throw) (JNIEnv *env, jthrowable obj);
    • FatalError:致命异常,用于输出一个异常信息,并终止当前JVM实例(即退出程序; void (JNICALL *FatalError) (JNIEnv *env, const char *msg);
本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2019年7月31日 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 野指针简介
  • 避免野指针崩溃的方法
  • 异常
    • 异常产生原因
      • Java与JNI处理异常区别
        • 处理异常的方式
          • ExceptionCheck
          • ExceptionOccurred
        • 异常抛出工具类
          • 异常发生后释放资源
            • 总结
            相关产品与服务
            日志服务
            日志服务(Cloud Log Service,CLS)是腾讯云提供的一站式日志服务平台,提供了从日志采集、日志存储到日志检索,图表分析、监控告警、日志投递等多项服务,协助用户通过日志来解决业务运维、服务监控、日志审计等场景问题。
            领券
            问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档