然后下面的代码有一个意想不到的行为: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/