java - JNI 分配损坏的堆

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

然后下面的代码有一个意想不到的行为:File 参数在运行时变成了一个 DirectoryListingPerfTest。

我想 java 堆已损坏,我的 native 代码没有使用正确的参数调用它。

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 对象。这段代码是错误的:

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 值,一般需要创建一个全局引用。见:

关于java - JNI 分配损坏的堆,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/42796371/

相关文章:

java - WEB-INF 在 CLASSPATH 中吗?

java - 使用 selenium 为 Firefox 设置代理身份验证

java - 安卓服务 START_STICKY START_NOT_STICKY

c++ - 等待 DownloadFileAsync 与 ManualResetEvent c++/cli

java - 如何确定正在运行的 JVM 上的 MaxDirectMemorySize?

java - 从petclinic jpa中的EntityManager获取 session 对象

c++ - OpenCV:断言失败(src.checkVector(2,CV_32F)

c++ - "Warning : No return statement in function returning non-void"是什么意思?

java - 对象创建的细微差别会导致完全不同的对象生命周期?

java - String.getBytes 是否被安全使用?