【问题标题】:JNI allocation corrupt heapJNI 分配损坏堆
【发布时间】:2017-03-16 03:57:27
【问题描述】:

然后以下代码出现意外行为:File 参数在运行时变为 DirectoryListingPerfTest。

我认为 java 堆已损坏,我的本机代码没有使用正确的参数调用它。

这里是 Java 代码:

public class DirectoryListingPerfTest implements FileFilter {

   private final SortedSet<File> _dirs = new TreeSet<>();

   @SuppressWarnings("cast")
   @Override
   public boolean accept( File pathname ) {
      if( pathname instanceof File ) {
        _dirs.add( pathname );
      }
      else {
         System.err.println( pathname.getClass());
      }
      return false;
   }

   static native void find( String path, FileFilter listener );

   public static void main( String[] args ) {
      System.loadLibrary( "fs_DirectoryListingPerfTest" );
      final DirectoryListingPerfTest ff = new DirectoryListingPerfTest();
      long atStart, elapsed;
      atStart = System.nanoTime();
      find( "/usr", ff );
      elapsed = System.nanoTime() - atStart;
      System.err.printf( "native(1): %,9.2f ms\n", elapsed / 1.0E+06 );
      atStart = System.nanoTime();
      find( "/usr", ff );
      elapsed = System.nanoTime() - atStart;
      System.err.printf( "native(2): %,9.2f ms\n", elapsed / 1.0E+06 );
   }
}

这是 C++ JNI 代码:

static jclass    File;
static jmethodID FileCtor;

static void Java_fs_DirectoryListingPerfTest_find_r(
   JNIEnv *     env,
   const char * path,
   jobject      listener,
   jmethodID    accept )
{
   jstring jPath = env->NewStringUTF( path );
   jobject file  = env->NewObject( File, FileCtor, jPath );
   jobject gFile = env->NewGlobalRef( file );
   env->CallBooleanMethod( listener, accept, gFile );
   DIR * dir = opendir( path );
   if( dir ) {
      struct dirent * entry;
      while(( entry = readdir( dir )) != NULL ) {
         if(  ( 0 == strcmp( entry->d_name, "."  ))
            ||( 0 == strcmp( entry->d_name, ".." )))
         {
            continue;
         }
         if( entry->d_type == DT_DIR ) {
            char * buffer = (char *)malloc(
               strlen( path ) + 1 + strlen( entry->d_name ) + 1 );
            strcpy( buffer, path );
            strcat( buffer, "/" );
            strcat( buffer, entry->d_name );
            Java_fs_DirectoryListingPerfTest_find_r(
               env, buffer, listener, accept );
            free( buffer );
         }
      }
      closedir( dir );
   }
}

JNIEXPORT void JNICALL Java_fs_DirectoryListingPerfTest_find(
   JNIEnv * env,
   jclass   clazz,
   jstring  jpath,
   jobject  listener )
{
   if( File == NULL ) {
      File     = env->FindClass( "java/io/File" );
      FileCtor = env->GetMethodID( File, "<init>", "(Ljava/lang/String;)V");
   }
   jclass FileFilter = env->GetObjectClass( listener );
   jmethodID accept =
      env->GetMethodID( FileFilter, "accept", "(Ljava/io/File;)Z");
   const char * path       = env->GetStringUTFChars( jpath, JNI_FALSE );
   Java_fs_DirectoryListingPerfTest_find_r( env, path, listener, accept );
   env->ReleaseStringUTFChars( jpath, path );
}

这是执行跟踪:

native(1):    584,76 ms
class fs.DirectoryListingPerfTest
class fs.DirectoryListingPerfTest
... (a lot !)
class fs.DirectoryListingPerfTest
Exception in thread "main" java.lang.ClassCastException: fs.DirectoryListingPerfTest cannot be cast to java.lang.Comparable
    at java.util.TreeMap.put(TreeMap.java:565)
    at java.util.TreeSet.add(TreeSet.java:255)
    at fs.DirectoryListingPerfTest.accept(DirectoryListingPerfTest.java:16)
    at fs.DirectoryListingPerfTest.find(Native Method)
    at fs.DirectoryListingPerfTest.main(DirectoryListingPerfTest.java:91)

【问题讨论】:

    标签: java c++ jvm java-native-interface


    【解决方案1】:

    您不能在调用之间缓存 Java 对象。这段代码是错误的:

    static jclass    File;
    static jmethodID FileCtor;
    
    ...
    
       if( File == NULL ) {
          File     = env->FindClass( "java/io/File" );
          FileCtor = env->GetMethodID( File, "<init>", "(Ljava/lang/String;)V");
       }
    

    如果要缓存 Java 值,通常需要创建全局引用。见:

    【讨论】:

      【解决方案2】:

      感谢 Andrew,根据您的建议,我已经让 ant 测试了一个更正的 C++ 版本,希望对您有所帮助:

      namespace fs {
      
         class DirectoryListingPerfTest {
         private:
      
            JNIEnv *  _env;
            jclass    _File;
            jmethodID _FileCtor;
            jobject   _listener;
            jmethodID _accept;
      
         public:
      
            DirectoryListingPerfTest(
               JNIEnv *  env,
               jclass    File,
               jmethodID FileCtor,
               jobject   listener,
               jmethodID accept    )
            :
               _env     ( env ),
               _File    ( File ),
               _FileCtor( FileCtor ),
               _listener( listener ),
               _accept  ( accept )
            {}
      
            void find( const char * path ) {
               jstring jPath = _env->NewStringUTF( path );
               jobject file  = _env->NewObject( _File, _FileCtor, jPath );
               jobject gFile = _env->NewGlobalRef( file );
               _env->CallBooleanMethod( _listener, _accept, gFile );
               DIR * dir = opendir( path );
               if( dir ) {
                  struct dirent * entry;
                  while(( entry = readdir( dir )) != NULL ) {
                     if(  ( 0 == strcmp( entry->d_name, "."  ))
                        ||( 0 == strcmp( entry->d_name, ".." )))
                     {
                        continue;
                     }
                     if( entry->d_type == DT_DIR ) {
                        char * buffer = (char *)malloc(
                           strlen( path ) + 1 + strlen( entry->d_name ) + 1 );
                        strcpy( buffer, path );
                        strcat( buffer, "/" );
                        strcat( buffer, entry->d_name );
                        find( buffer );
                        free( buffer );
                     }
                  }
                  closedir( dir );
               }
            }
      
         private:
            DirectoryListingPerfTest( const DirectoryListingPerfTest & );
            DirectoryListingPerfTest & operator = ( const DirectoryListingPerfTest & );
         };
      }
      
      JNIEXPORT void JNICALL Java_fs_DirectoryListingPerfTest_find(
         JNIEnv * env,
         jclass   clazz,
         jstring  jpath,
         jobject  listener )
      {
         jclass       File       = env->FindClass( "java/io/File" );
         jmethodID    FileCtor   = env->GetMethodID( File, "<init>", "(Ljava/lang/String;)V");
         jclass       FileFilter = env->GetObjectClass( listener );
         jmethodID    accept     = env->GetMethodID( FileFilter, "accept", "(Ljava/io/File;)Z");
         const char * path       = env->GetStringUTFChars( jpath, JNI_FALSE );
         fs::DirectoryListingPerfTest( env, File, FileCtor, listener, accept ).find( path );
         env->ReleaseStringUTFChars( jpath, path );
      }
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2019-09-23
        • 1970-01-01
        • 2012-07-27
        • 2010-11-15
        • 2011-12-03
        • 2011-08-03
        相关资源
        最近更新 更多