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

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

Ответ 1

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

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

ИЗМЕНИТЬ

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

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

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

Ответ 2

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

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

Ответ 3

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

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

Ответ 4

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

Ответ 5

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

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

Ответ 6

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

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

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

Ответ 7

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

Как уже указывалось другими, JNA для удобства. При использовании JNA вам не нужно компилировать или писать собственный код. Загрузочный загрузчик библиотеки JNA также является одним из лучших/самых простых в использовании, которые я когда-либо видел. К сожалению, вы не можете использовать его для JNI. (Вот почему я написал альтернативу для System.loadLibrary(), которая использует соглашение о трассировке JNA и поддерживает плавную загрузку из пути к классам (например, банок).)

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

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

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

Ответ 8

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

Ответ 9

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

Ответ 10

В моем конкретном приложении JNI оказалось намного проще в использовании. Мне нужно было читать и записывать непрерывные потоки в последовательный порт и из него - и ничего больше. Вместо того, чтобы пытаться изучить очень сложную инфраструктуру в JNA, я обнаружил, что гораздо проще создать прототип нативного интерфейса в Windows с помощью специальной библиотеки DLL, которая экспортировала всего шесть функций:

  1. DllMain (требуется для взаимодействия с Windows)
  2. OnLoad (просто выполняет OutputDebugString, чтобы я мог знать, когда присоединяется код Java)
  3. OnUnload (то же самое)
  4. Открыть (открывает порт, начинает читать и писать темы)
  5. QueueMessage (очереди данных для вывода потоком записи)
  6. GetMessage (ожидает и возвращает данные, полученные потоком чтения с момента последнего вызова)