美文网首页
如何排查JNI调用和内存分配问题

如何排查JNI调用和内存分配问题

作者: allanYan | 来源:发表于2022-09-15 17:16 被阅读0次

    最近在工作中遇到GC问题,发现GC日志中存在很多[GC pause (GCLocker Initiated GC) (young)的日志,另外还发现特别多的大对象分配:

     56123.146: [G1Ergonomics (Concurrent Cycles) request concurrent cycle initiation, reason: occupancy higher than threshold, occupancy: 2415919104 bytes, allocation request: 3718600 bytes, threshold: 2415919095 bytes (45.00 %), source: concurrent humongous allocation]
    

    在排查过程中用到了些排查工具,特记录下备忘;

    snappy

    项目中使用了snappy做压缩和解压缩,系统的负载很高:

    JNIEXPORT jint JNICALL Java_org_xerial_snappy_SnappyNative_rawCompress__Ljava_lang_Object_2IILjava_lang_Object_2I
      (JNIEnv * env, jobject self, jobject input, jint inputOffset, jint inputLen, jobject output, jint outputOffset)
    {
        char* in = (char*) env->GetPrimitiveArrayCritical((jarray) input, 0);
        char* out = (char*) env->GetPrimitiveArrayCritical((jarray) output, 0);
        if(in == 0 || out == 0) {
            // out of memory
            if(in != 0) {
                env->ReleasePrimitiveArrayCritical((jarray) input, in, 0);
            }
            if(out != 0) {
                env->ReleasePrimitiveArrayCritical((jarray) output, out, 0);
            }
            throw_exception(env, self, 4);
            return 0;
        }
    
        size_t compressedLength;
        snappy::RawCompress(in + inputOffset, (size_t) inputLen, out + outputOffset, &compressedLength);
    
        env->ReleasePrimitiveArrayCritical((jarray) input, in, 0);
        env->ReleasePrimitiveArrayCritical((jarray) output, out, 0);
    
        return (jint) compressedLength;
    }
    
    JNI_ENTRY(void*, jni_GetPrimitiveArrayCritical(JNIEnv *env, jarray array, jboolean *isCopy))
      JNIWrapper("GetPrimitiveArrayCritical");
      GC_locker::lock_critical(thread);
      if (isCopy != NULL) {
        *isCopy = JNI_FALSE;
      }
      oop a = JNIHandles::resolve_non_null(array);
      BasicType type;
      if (a->is_objArray()) {
        type = T_OBJECT;
      } else {
        type = TypeArrayKlass::cast(a->klass())->element_type();
      }
      void* ret = arrayOop(a)->base(type);
      return ret;
    JNI_END
    
    
    JNI_ENTRY(void, jni_ReleasePrimitiveArrayCritical(JNIEnv *env, jarray array, void *carray, jint mode))
      JNIWrapper("ReleasePrimitiveArrayCritical");
      // The array, carray and mode arguments are ignored
      GC_locker::unlock_critical(thread);
    
    JNI_END
    
    

    可以看到snappy最终使用了GC_locker::lock_critical(thread)和GC_locker::unlock_critical(thread)来进出JNI临界区;

    JNI相关日志参数

    1. verbose:jni 打印JNI调用日志
    2. PrintJNIGCStalls: 打印线程进出JNI临界区日志

    async-profiler

    项目地址:https://github.com/jvm-profiling-tools/async-profiler

    常用命令:

    1. ./profiler.sh -d 20 -f 2449-alloc.svg -e alloc 2449
    2. ./profiler.sh -d 10 -f humongous.jfr -e alloc 2449

    对于jfr文件,可以用java自带的jfr命令来进行分析

    jfr print humongous.jfr
    jfr summary humongous.jfr
    jfr print --events jdk.ObjectAllocationOutsideTLAB humongous.jfr
    

    相关文章

      网友评论

          本文标题:如何排查JNI调用和内存分配问题

          本文链接:https://www.haomeiwen.com/subject/lqfdortx.html