JNI分配损坏的堆

时间:2017-03-14 20:55:45

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

以下代码有一个意外的行为:File参数在运行时变为DirectoryListingPerfTest。

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

这是Java代码:

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 );
}

这是C ++ JNI代码:

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)

这是执行追踪:

<html>
    <script language="javascript">
        function encrypt() {
            var texttoencrypt = document.getElementById("text").innerHTML;
            var encrypted = btoa(texttoencrypt)
            document.getElementById("text").innerHTML = encrypted;
        }
    </script>
    <head></head>
    <title>Base64 Encrypt</title>
    <body>
        <textarea id="text" rows="4" cols="50" autofocus placeholder="Put text here."></textarea>
        <br>
        <button onclick="encrypt()">Encrypt</button>
        <button onclick="decrypt()">Decrypt</button>
    </body>
</html>

2 个答案:

答案 0 :(得分: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值,通常需要创建全局引用。参见:

答案 1 :(得分:0)

感谢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 );
}