Как уже писали другие
- Вы можете без проблем хранить
jmethodID
в статической переменной C ++
- Вы можете хранить локальные
jobject
или jclass
в статической переменной C ++ после преобразования их в глобальные объекты, вызывая env->NewGloablRef()
Я просто хочу добавить дополнительную информацию здесь:
Основная причина хранения jclass в статической переменной C ++ заключается в том, что вы думаете, что это проблема с производительностью - каждый раз вызывать env->FindClass()
.
Но я измерил скорость из FindClass()
с помощью счетчика производительности с QueryPerformanceCounter()
API в Windows. И результат был удивительным:
На моем компьютере с процессором 3,6 ГГц исполнение
jcass p_Container = env->FindClass("java/awt/Container");
занимает от 0,01 мс до 0,02 мс. Это невероятно быстро. Я посмотрел в исходный код Java, и они используют словарь, где хранятся классы. Кажется, это реализовано очень эффективно.
Я протестировал еще несколько классов и вот результат:
Elapsed 0.002061 ms for java/net/URL
Elapsed 0.044390 ms for java/lang/Boolean
Elapsed 0.019235 ms for java/lang/Character
Elapsed 0.018372 ms for java/lang/Number
Elapsed 0.017931 ms for java/lang/Byte
Elapsed 0.017589 ms for java/lang/Short
Elapsed 0.017371 ms for java/lang/Integer
Elapsed 0.015637 ms for java/lang/Double
Elapsed 0.018173 ms for java/lang/String
Elapsed 0.015895 ms for java/math/BigDecimal
Elapsed 0.016204 ms for java/awt/Rectangle
Elapsed 0.016272 ms for java/awt/Point
Elapsed 0.001817 ms for java/lang/Object
Elapsed 0.016057 ms for java/lang/Class
Elapsed 0.016829 ms for java/net/URLClassLoader
Elapsed 0.017807 ms for java/lang/reflect/Field
Elapsed 0.016658 ms for java/util/Locale
Elapsed 0.015720 ms for java/lang/System
Elapsed 0.014669 ms for javax/swing/JTable
Elapsed 0.017276 ms for javax/swing/JComboBox
Elapsed 0.014777 ms for javax/swing/JList
Elapsed 0.015597 ms for java/awt/Component
Elapsed 0.015223 ms for javax/swing/JComponent
Elapsed 0.017385 ms for java/lang/Throwable
Elapsed 0.015089 ms for java/lang/StackTraceElement
Приведенные выше значения взяты из потока диспетчера событий Java. Если я выполню тот же код в собственном потоке Windows, который был создан мной CreateThread()
, он будет работать даже в 10 раз быстрее . Почему?
Так что, если вы не вызываете FindClass()
очень часто, то нет абсолютно никаких проблем с вызовом его по требованию при вызове вашей функции JNI вместо создания глобальной ссылки и сохранения ее в статической переменной.
Другая важная тема - thread thread . В Java каждый поток имеет свою собственную независимую JNIEnv
структуру.
- Global
jobject
или jclass
действительны в любом потоке Java.
- Локальные объекты действительны только в одном вызове функции в
JNIEnv
вызывающего потока и собирают мусор, когда код JNI возвращается в Java.
Теперь это зависит от потоков, которые вы используете: если вы регистрируете свою функцию C ++ с помощью env->RegisterNatives()
и Java-код вызывает ваши функции JNI, то вы должны хранить все объекты, которые вы хотите использовать позже, как глобальные объекты в противном случае они будут собирать мусор.
Но если вы создадите свой собственный поток с помощью CraeteThread()
API (в Windows) и получите структуру JNIEnv
, вызвав AttachCurrentThreadAsDaemon()
, тогда будут применяться совершенно другие правила: поскольку это ваш собственный поток, он никогда не возвращает управление В Java сборщик мусора никогда не очистит созданные вами объекты в потоке, и вы даже можете без проблем хранить локальные объекты в статических переменных C ++! (Но к ним нельзя получить доступ из других потоков). В этом случае крайне важно, чтобы вы очистили ВСЕ ваши локальные экземпляры вручную с помощью env->DeleteLocalRef()
, иначе у вас будет утечка памяти.
Я настоятельно рекомендую загрузить ВСЕ локальные объекты в класс-оболочку, который вызывает DeleteLocalRef()
в своем деструкторе. Это пуленепробиваемый способ избежать утечек памяти.
Разработка кода JNI может быть очень громоздкой, и вы можете получить сбои, которые вы не понимаете. Чтобы найти причину сбоя, откройте окно DOS и запустите приложение Java с помощью следующей команды:
java -Xcheck:jni -jar MyApplication.jar
тогда вы увидите, какие проблемы произошли в коде JNI. Например:
FATAL ERROR in native method: Bad global or local ref passed to JNI
и вы найдете трассировку стека в файле журнала, который Java создает в той же папке, где у вас есть файл JAR:
#
# A fatal error has been detected by the Java Runtime Environment:
#
# EXCEPTION_ACCESS_VIOLATION (0xc0000005) at pc=0x6e8655d5, pid=4692, tid=4428
#
etc...