Использовать JNI вместо JNA для вызова нативного кода? - PullRequest
112 голосов
/ 12 октября 2009

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

Ответы [ 9 ]

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

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

EDIT

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

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

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

29 голосов
/ 13 октября 2009

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

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

8 голосов
/ 13 октября 2009
  1. Вы пишете код несколько лет назад, до того, как появилась JNA, или планируете использовать JRE до версии 1.4.
  2. Код, с которым вы работаете, отсутствует в DLL \ SO.
  3. Вы работаете над кодом, несовместимым с LGPL.

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

7 голосов
/ 14 декабря 2011

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

5 голосов
/ 12 октября 2009

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

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

3 голосов
/ 14 июня 2016

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

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

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

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

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

3 голосов
/ 28 ноября 2015

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

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

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

2 голосов
/ 06 августа 2014

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

1 голос
/ 18 марта 2010

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

...