Использовать JNI вместо JNA для вызова собственного кода?

JNA кажется немного проще использовать для вызова собственного кода по сравнению с JNI. В каких случаях вы бы использовали JNI над JNA?

9 ответов


  1. JNA не поддерживает сопоставление классов C++, поэтому, если вы используете библиотеку C++, вам понадобится оболочка jni
  2. Если вам нужно много копировать памяти. Например, вы вызываете один метод, который возвращает вам большой байтовый буфер, вы что-то в нем меняете, затем вам нужно вызвать другой метод, который использует этот байтовый буфер. Для этого вам потребуется скопировать этот буфер с c на java, а затем скопировать его обратно с java на c. В этом случае jni выиграет в производительности, потому что вы можете сохранить и измените этот буфер в c, без копирования.

это проблемы с которыми я столкнулся. Может, есть что-то еще. Но в целом производительность не так уж отличается между jna и jni, поэтому везде, где вы можете использовать JNA, используйте его.

редактировать

этот ответ, кажется, довольно популярны. Итак, вот некоторые дополнения:

  1. Если вам нужно сопоставить C++ или COM, есть библиотека Оливера Чафика, создателя JNAerator, называется BridJ. Это еще молодая библиотека, но она имеет много интересных особенностей:
    • динамическое взаимодействие C / C++ / COM: вызовите методы c++, создайте объекты C++ (и подклассы классов C++ из Java!)
    • простые сопоставления типов с хорошим использованием дженериков (включая гораздо более приятную модель для указателей)
    • полная поддержка JNAerator
    • работает на Windows, Linux, MacOS X, Solaris, Android
  2. что касается памяти копирование, я считаю, что JNA поддерживает прямые ByteBuffers, поэтому копирования памяти можно избежать.

Итак, я по-прежнему считаю, что везде, где это возможно, лучше использовать JNA или BridJ и вернуться к jni, если производительность критична, потому что, если вам нужно часто вызывать собственные функции, хит производительности заметен.


трудно ответить на такой общий вопрос. Я полагаю, что наиболее очевидным отличием является то, что с JNI преобразование типов реализовано на родной стороне границы Java/native, в то время как с JNA преобразование типов реализовано на Java. Если вы уже чувствуете себя вполне комфортно с программированием на C и должны реализовать некоторый собственный код самостоятельно, я бы предположил, что JNI не будет казаться слишком сложным. Если вы программист Java и вам нужно только вызвать стороннюю собственную библиотеку, использование JNA, вероятно, самый простой способ избежать, возможно, не столь очевидных проблем с JNI.

хотя я никогда не сравнивал никаких различий, я бы из-за дизайна, по крайней мере, предположил, что преобразование типов с JNA в некоторых ситуациях будет работать хуже, чем с JNI. Например, при передаче массивов JNA преобразует их из Java в native в начале каждого вызова функции и обратно в конце вызова функции. С JNI, вы можете контролировать себя, когда родной создается" представление " массива, потенциально создающее представление только части массива, сохраняйте представление в нескольких вызовах функций и в конце отпустите представление и решите, хотите ли вы сохранить изменения (потенциально требующие копирования данных обратно) или отменить изменения (копия не требуется). Я знаю, что вы можете использовать собственный массив через вызовы функций с JNA, используя класс памяти, но это также потребует копирования памяти, что может быть ненужным с JNI. Разница может и не быть уместно, но если ваша первоначальная цель-повысить производительность приложения, реализуя его части в собственном коде, использование более эффективной технологии моста кажется не самым очевидным выбором.


  1. вы пишете код несколько лет назад, прежде чем была JNA или нацелены на pre 1.4 JRE.
  2. код, с которым вы работаете, не находится в DLL\SO.
  3. вы работаете над кодом, несовместимым с LGPL.

это единственное что я могу придумать с верхней части моей головы, хотя я не являюсь активным пользователем ни. Также кажется, что вы можете избежать JNA, если хотите лучший интерфейс, чем тот, который они предоставляют, но вы можете кодировать вокруг этого на java.


кстати, в одном из наших проектов мы сохранили очень маленький отпечаток ноги JNI. Мы использовали буферы протокола для представления наших объектов домена и, таким образом, имели только одну собственную функцию для моста Java и C (тогда, конечно, эта функция C вызывала бы кучу других функций).


Это не прямой ответ, и у меня нет опыта работы с JNA, но, когда я смотрю на проекты с использованием JNA и увидеть имена, такие как SVNKit, IntelliJ IDEA, NetBeans IDE и т. д., Я склонен полагать, что это довольно приличная библиотека.

на самом деле, я определенно думаю, что я бы использовал JNA вместо JNI, когда мне пришлось, так как это действительно выглядит проще, чем JNI (который имеет скучный процесс разработки). Жаль, что в это время ДЖНУ не выпустили.


Если вы хотите производительность JNI, но устрашены его сложностью, вы можете использовать инструменты, которые генерируют привязки JNI автоматически. Например, Дженет (отказ от ответственности: я написал его) позволяет смешивать код Java и C++ в одном исходном файле и, например, совершать вызовы с C++ на Java с использованием стандартного синтаксиса Java. Например, вот как вы печатаете строку C в стандартный вывод Java:

native "C++" void printHello() {
    const char* helloWorld = "Hello, World!";
    `System.out.println(#$(helloWorld));`
}

Джанет затем переводит встроенную в backtick Java в соответствующие вызовы JNI.


Я исследовал JNI и JNA для сравнения производительности, потому что нам нужно было решить, один из них вызвать dll в проекте, и у нас было ограничение в реальном времени. Результаты показали, что JNI имеет большую производительность, чем JNA(примерно в 40 раз). Возможно, есть трюк для лучшей производительности в JNA, но он очень медленный для простого примера.


Я на самом деле сделал несколько простых тестов с JNI и JNA.

Как уже указывали другие, JNA для удобства. Вам не нужно компилировать или писать собственный код при использовании JNA. Родной загрузчик библиотеки JNA также является одним из лучших/самых простых в использовании, которые я когда-либо видел. К сожалению, вы не можете использовать его для JNI. (Вот почему я написал Альтернатива для системы.функции LoadLibrary() который использует соглашение о пути JNA и поддерживает плавную загрузку из пути к классам (т. е. файлы jar.))

производительность JNA, однако, может быть намного хуже, чем у JNI. Я сделал очень простой тест, который вызвал простую собственную целочисленную инкрементную функцию " return arg + 1;". Тесты, проведенные с jmh, показали, что вызовы JNI этой функции в 15 раз быстрее, чем JNA.

более "сложный" пример, где собственная функция суммирует целочисленный массив из 4 значений, все еще показал, что производительность JNI в 3 раза быстрее, чем JNA. Уменьшенное преимущество было, вероятно, потому, что о том, как вы получаете доступ к массивам в JNI: мой пример создал некоторые вещи и выпустил его снова во время каждой операции суммирования.

код и результаты тестов можно найти в github.


Если я чего-то не упускаю, не является ли основное различие между JNA и JNI, что с JNA вы не можете вызвать Java-код из собственного (C) кода?