JNA 대신 JNI를 사용하여 네이티브 코드를 호출 하시겠습니까?


115

JNA는 JNI에 비해 네이티브 코드를 호출하는 데 사용하기가 약간 더 쉽습니다. 어떤 경우에 JNA를 통해 JNI를 사용 하시겠습니까?


JNI보다 JNA를 선택한 한 가지 중요한 요소는 JNA가 네이티브 라이브러리를 수정할 필요가 없다는 것입니다. 자바로 작업 할 수 있도록 네이티브 라이브러리를 사용자 정의해야하는 것은 우리에게 큰 일이 아닙니다.
kvr

답변:


126
  1. JNA는 C ++ 클래스 매핑을 지원하지 않으므로 C ++ 라이브러리를 사용하는 경우 jni 래퍼가 필요합니다.
  2. 많은 메모리 복사가 필요한 경우. 예를 들어, 큰 바이트 버퍼를 반환하는 하나의 메서드를 호출하고 그 안의 내용을 변경 한 다음이 바이트 버퍼를 사용하는 다른 메서드를 호출해야합니다. 이 버퍼를 c에서 java로 복사 한 다음 다시 java에서 c로 복사해야합니다. 이 경우 복사없이 c에서이 버퍼를 유지하고 수정할 수 있기 때문에 jni가 성능면에서 승리합니다.

이것이 내가 만난 문제입니다. 아마도 더있을 것입니다. 그러나 일반적으로 jna와 jni의 성능은 크게 다르지 않으므로 JNA를 사용할 수있는 곳이면 어디에서나 사용하십시오.

편집하다

이 답변은 꽤 인기있는 것 같습니다. 다음은 몇 가지 추가 사항입니다.

  1. C ++ 또는 COM을 매핑해야하는 경우 JNAerator를 만든 Oliver Chafic의 라이브러리 인 BridJ가 있습니다. 아직은 젊은 도서관이지만 흥미로운 기능이 많이 있습니다.
    • 동적 C / C ++ / COM interop : C ++ 메서드 호출, C ++ 개체 생성 (및 Java에서 C ++ 클래스 하위 클래스!)
    • 제네릭을 잘 사용하는 간단한 유형 매핑 (포인터에 대한 훨씬 더 좋은 모델 포함)
    • 완전한 JNAerator 지원
    • Windows, Linux, MacOS X, Solaris, Android에서 작동
  2. 메모리 복사에 관해서는 JNA가 직접 ByteBuffer를 지원하므로 메모리 복사를 피할 수 있습니다.

따라서 가능한 한 JNA 또는 BridJ를 사용하고 성능이 중요한 경우 jni로 되 돌리는 것이 더 낫다고 생각합니다. 기본 함수를 자주 호출해야하는 경우 성능 저하가 눈에 띄기 때문입니다.


6
동의하지 않습니다. JNA에는 많은 오버 헤드가 있습니다. 편리함은 시간이 중요하지 않은 코드에서 사용할 가치가 있지만
Gregory Pakosz

3
Android 프로젝트에 BridJ를 사용하기 전에 다운로드 페이지 에서 직접 인용 할 때주의해야 합니다 . "BridJ는 부분적으로 Android / arm 에뮬레이터 (SDK 포함)에서 작동하며, 아마도 실제 장치 (
예상

1
@StockB : C ++ 개체를 전달하거나 C ++ 개체에 대한 메서드를 호출해야하는 API가있는 라이브러리가있는 경우 JNA는이를 수행 할 수 없습니다. 바닐라 C 메서드 만 호출 할 수 있습니다.
Denis Tulskiy 2013 년

2
내가 이해하는 한 JNI는 전역 JNIEXPORT함수 만 호출 할 수 있습니다 . 현재 JNI를 사용하는 JavaCpp를 옵션으로 탐색하고 있지만 바닐라 JNI가이를 지원한다고 생각하지 않습니다. 내가 간과하고있는 바닐라 JNI를 사용하여 C ++ 멤버 함수를 호출하는 방법이 있습니까?
StockB


29

이러한 일반적인 질문에 답하기는 어렵습니다. 가장 분명한 차이점은 JNI를 사용하면 유형 변환이 Java / 네이티브 경계의 기본 측면에서 구현되는 반면 JNA에서는 유형 변환이 Java로 구현된다는 것입니다. 이미 C 프로그래밍에 익숙하고 네이티브 코드를 직접 구현해야한다면 JNI가 너무 복잡해 보이지 않을 것이라고 생각합니다. Java 프로그래머이고 타사 네이티브 라이브러리 만 호출해야하는 경우 JNA를 사용하는 것이 JNI와 관련하여 명확하지 않은 문제를 방지하는 가장 쉬운 방법 일 것입니다.

차이점을 벤치마킹 한 적이 없지만 디자인 때문에 적어도 일부 상황에서 JNA를 사용한 유형 변환이 JNI보다 성능이 나쁘다고 가정합니다. 예를 들어 배열을 전달할 때 JNA는 각 함수 호출의 시작과 함수 호출의 끝에서이를 Java에서 원시로 변환합니다. JNI를 사용하면 어레이의 기본 "뷰"가 생성 될 때 자신을 제어 할 수 있으며 잠재적으로 어레이의 일부 뷰만 생성하고 여러 함수 호출에서 뷰를 유지하며 마지막에 뷰를 해제하고 원하는지 결정할 수 있습니다. 변경 사항을 유지하거나 (잠재적으로 데이터를 다시 복사해야 함) 변경 사항을 삭제합니다 (복사 할 필요 없음). Memory 클래스를 사용하여 JNA로 함수 호출간에 네이티브 배열을 사용할 수 있다는 것을 알고 있지만 메모리 복사도 필요합니다. JNI에서는 불필요 할 수 있습니다. 차이는 관련이 없을 수 있지만 원래 목표가 네이티브 코드로 애플리케이션의 일부를 구현하여 애플리케이션 성능을 높이는 것이라면 성능이 떨어지는 브리지 기술을 사용하는 것이 가장 확실한 선택이 아닌 것 같습니다.


8
  1. JNA가 있기 몇 년 전에 코드를 작성하고 있거나 1.4 이전 JRE를 목표로하고 있습니다.
  2. 작업중인 코드가 DLL \ SO에 없습니다.
  3. LGPL과 호환되지 않는 코드를 작업 중입니다.

그것은 내가 두 가지를 많이 사용하지는 않지만 머릿속에서 떠 올릴 수있는 유일한 것입니다. 또한 그들이 제공하는 것보다 더 나은 인터페이스를 원한다면 JNA를 피할 수있는 것처럼 보이지만 Java로 코딩 할 수 있습니다.


9
2에 대해 동의하지 않습니다. 정적 lib를 동적 lib로 변환하는 것은 쉽습니다. 내 질문 abput it stackoverflow.com/questions/845183/…
jb를

3
JNA 4.0부터 JNA는 LGPL 2.1 및 Apache License 2.0에 따라 이중 라이센스가 부여되며 귀하가 선택하는 것은 귀하에게
달려

8

그건 그렇고, 우리 프로젝트 중 하나에서 우리는 매우 작은 JNI 발자국을 유지했습니다. 우리는 도메인 객체를 표현하기 위해 프로토콜 버퍼를 사용했기 때문에 Java와 C를 연결하는 네이티브 함수가 하나뿐이었습니다 (물론 C 함수가 다른 함수를 호출합니다).


5
따라서 메서드 호출 대신 메시지 전달이 있습니다. JNI와 JNA, BridJ에 꽤 많은 시간을 투자했지만 곧 모두가 너무 무서워집니다.
Ustaman Sangat 2012

5

직접적인 대답은 아니고 JNA에 대한 경험이 없지만 JNA를 사용 하는 프로젝트 에서 SVNKit, IntelliJ IDEA, NetBeans IDE 등과 같은 이름을 보면 꽤 괜찮은 라이브러리라고 생각하는 경향이 있습니다.

실제로 JNI (지루한 개발 프로세스가있는)보다 간단 해 보이기 때문에 JNI 대신 JNA를 사용했을 것이라고 생각합니다. 안타깝게도 JNA는 현재 출시되지 않았습니다.


3

JNI 성능을 원하지만 복잡성으로 인해 어려움을 겪는 경우 JNI 바인딩을 자동으로 생성하는 도구 사용을 고려할 수 있습니다. 예를 들어, JANET (면책 조항 : 내가 작성)을 사용하면 단일 소스 파일에 Java 및 C ++ 코드를 혼합 할 수 있습니다. 예를 들어 표준 Java 구문을 사용하여 C ++에서 Java로 호출 할 수 있습니다. 예를 들어 다음은 Java 표준 출력에 C 문자열을 인쇄하는 방법입니다.

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

그런 다음 JANET은 백틱이 포함 된 Java를 적절한 JNI 호출로 변환합니다.


3

실제로 JNI와 JNA로 몇 가지 간단한 벤치 마크를 수행했습니다.

다른 사람들이 이미 지적했듯이 JNA는 편의를위한 것입니다. JNA를 사용할 때 네이티브 코드를 컴파일하거나 작성할 필요가 없습니다. JNA의 기본 라이브러리 로더는 내가 본 것 중 가장 사용하기 쉽고 사용하기 쉬운 것 중 하나입니다. 슬프게도 JNI에는 사용할 수없는 것 같습니다. (그래서 JNA의 경로 규칙을 사용하고 클래스 경로 (예 : jar)에서 원활한로드를 지원하는 System.loadLibrary ()에 대한 대안을 작성 했습니다 .)

그러나 JNA의 성능은 JNI의 성능보다 훨씬 떨어질 수 있습니다. 간단한 네이티브 정수 증가 함수 "return arg + 1;"을 호출하는 매우 간단한 테스트를 만들었습니다. jmh로 수행 한 벤치 마크에 따르면 해당 함수에 대한 JNI 호출이 JNA보다 15 배 빠릅니다.

네이티브 함수가 4 개 값의 정수 배열을 합산하는보다 "복잡한"예에서는 여전히 JNI 성능이 JNA보다 3 배 빠릅니다. 감소 된 이점은 아마도 JNI에서 배열에 액세스하는 방법 때문일 것입니다. 제 예에서는 일부 항목을 만들고 각 합산 작업 중에 다시 릴리스했습니다.

코드 및 테스트 결과는 github에서 찾을 수 있습니다 .


2

성능 비교를 위해 JNI와 JNA를 조사했습니다. 프로젝트에서 dll을 호출하기 위해 둘 중 하나를 결정해야했고 실시간 제약이 있었기 때문입니다. 그 결과 JNI가 JNA보다 약 40 배 더 우수한 성능을 보였습니다. JNA에서 더 나은 성능을위한 트릭이있을 수 있지만 간단한 예제에서는 매우 느립니다.


1

내가 뭔가를 놓치고 있지 않는 한 JNA와 JNI의 주요 차이점은 JNA를 사용하면 네이티브 (C) 코드에서 Java 코드를 호출 할 수 없다는 것입니까?


6
할 수 있습니다. C 측의 함수 포인터에 해당하는 콜백 클래스가 있습니다. void register_callback (void (*) (const int)); public static native void register_callback (MyCallback arg1); 여기서 MyCallback은 com.sun.jna.Callback을 단일 메소드로 확장하는 인터페이스입니다. void apply (int value);
Ustaman Sangat 2011

이 코멘트도 즐겁게 할 수 있습니다 @Augusto
swiftBoy

0

내 특정 응용 프로그램에서 JNI는 사용하기 훨씬 쉬웠습니다. 직렬 포트에서 연속 스트림을 읽고 쓸 필요가있었습니다. JNA에서 매우 관련된 인프라를 배우려고하는 것보다 6 개의 함수 만 내 보낸 특수 목적 DLL을 사용하여 Windows에서 기본 인터페이스를 프로토 타입하는 것이 훨씬 쉽다는 것을 알았습니다.

  1. DllMain (Windows와의 인터페이스에 필요)
  2. OnLoad (Java 코드가 연결될 때 알 수 있도록 OutputDebugString 만 수행함)
  3. OnUnload (동일)
  4. 열기 (포트 열기, 읽기 및 쓰기 스레드 시작)
  5. QueueMessage (쓰기 스레드의 출력을 위해 데이터를 큐에 넣음)
  6. GetMessage (마지막 호출 이후 읽기 스레드에서 수신 한 데이터를 기다리고 반환)
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.