JNI 는 기존 C/C++ 함수를 호출하는데 사용한다고 하면서, 왜?

  • #3768197
    JNI 73.***.140.60 1086

    나무위키, 유투브 JNI 영상들, 기타 웹사이트 등등,
    JNI 를 설명하는 모든 곳에서, 가령, https://www.baeldung.com/jni, 에서
    자바에서 호출할 것을 가정해서 아래와 같이 JNIEXPORT 로 함수를 정의하는
    예제만 보여줍니다.

    JNIEXPORT void JNICALL Java_com_baeldung_jni_HelloWorldJNI_sayHello
    (JNIEnv* env, jobject thisObject) {
    std::cout << “Hello from C++ !!” << std::endl;
    }

    이미 존재하는 방대한 C/C++ 함수들이 이렇게 정의된게 아닐텐데,
    왜 모든 예제는 자바에서 호출할 것을 가정하고 함수를 정의하는 것만
    보여주는 걸까요?

    잘못 이해하고 있다면, 한 수 지도 부탁드립니다.

    참고:

    나무위키, https://namu.wiki/w/JNI 에서 말하는 JNI 사용 목적:
    이미 존재하는 방대한 C/C++ 코드를 활용하기 위해.
    Java로 구현하면 너무 느린 기능의 속도 향상을 위해.
    JVM에서 지원하지 않는 운영체제의 기능을 호출하기 위해.

    • P 23.***.51.218

      일종의 wrapper입니다. Java – C/C++ 간의 타입 변환 등을 수행하고 내부에서 C/C++ 함수를 호출합니다.

    • Java 174.***.145.146

      JNI가 Java Native Interface이기 때문에 Java에서 부르는걸 가정하는 거겠지요.

      • JNI 73.***.140.60

        아, 그래서, GNU Scientific Library in C/C++ 처럼, JavaCPP 라는 Wrapper 를 제공하는 건가요?

        https://www.gnu.org/software/gsl/#development

        저는 standard call 로 정의된 C/C++ 함수들을 자바에서 호출하는 방법으로 JNI 가 존재하는 줄 알았습니다.

        감사합니다.

    • fdff 131.***.51.64

      JNI는 Native Language 입니다. 즉 플랫폼 별로 컴파일을 다시 하셔야 하죠.

      원래 Java는 Interpret Language 입니다. 그래서 한 번 .class로 컴파일 하시면 어떤 플랫폼이던 JRE를 설치하신 플랫폼에서는 다시컴파일을 하지 않으셔도 실행이 가능합니다. 즉, JRE를 통해서 class가 각각의 플랫폼을 위한 native language로 변환된 후 실행되는 거죠. 그래서 class는 윈도우, 맥, 리눅스를 가리지 않고 한 번 컴파일을 하시면 모두 실행이 가능합니다.

      반면 C/C++는 각 플랫폼을 위해서 따로 컴파일을 해 주고 각각 실행파일을 생성해 주셔야 합니다. 이를 Native Language라고 하죠. 그래서 중간 과정인 JRE가 없기 때문에 실행 속도나 메모리 점유면에서 더 효율적인 면이 있습니다.

      JAVA가 아무래도 속도면에서 비효율적인게 있기 때문에 자주 쓰거나 빠른 실행이 필요한 함수들 같은 경우에는 JRE를 통하지 않고 실행실킬 수 있는 JNI라는 기능을 지원합니다. 즉 함수들이 .class로 컴파일 되는게 아닌 C/C++ 형식으로 컴파일 되는거죠. 그렇기 때문에 실행속도 면에서는 효율적이지만 JNI로 구현된 부분들은 다른 C/C++ 프로그램과 같이 각각의 플랫폼 별로 컴파일을 다시 해 주셔야 합니다. 즉 Portability를 버리는 대신에 efficiency를 가지게 되는거죠.

      답변이 되었으면 좋겠습니다.

    • Chisato 140.***.198.159

      그냥 지나가다 하는 얘긴데요, JNI쓴다고 꼭 빨라지는건 아닙니다. 이것 자체에도 오버헤드가 있어서요. 특히 데이타를 주고 받게 된다면 안좋습니다. 아예 native byte buffer에서 애초에 데이타를 받아서 한다면 모를까, java heap에서/으로 카피하거나 하다보면 성능 확 떨어집니다.

    • rui 17.***.221.234

      “이미 존재하는 방대한 C/C++ 함수들이 이렇게 정의된게 아닐텐데,
      왜 모든 예제는 자바에서 호출할 것을 가정하고 함수를 정의하는 것만
      보여주는 걸까요?”

      예를 들어 C++로 된 SDK가 있는데 100개 함수가 있다고 가정해 봅시다. (보통은 클래스로 되어 있겠지만, 일단 그냥 함수들이라 가정하고)
      그 중에 님이 Java 쓰고 싶은 함수가 5개면, 그 5개 함수를 호출하는 JNIExport 함수들을 님이 작성해야 합니다.
      그리고 님의 Java 프로그램은 님이 정의한 그 함수들을 호출해서, 결과적으로 C++ SDK에 접금하는 거죠.

      님이 붙힌 예를 들어 생각해 보면, 자바에 스크린으로 프린트하는 함수가 없다고 가정해 보시죠. 그럴 경우에, C++ STL 에 정의된 std::out 함수를 위와 같이 JNIExport 로 wrap 하면, 님은 자바에서도 프린트를 할 수 있게 되죠.

      • JNI 73.***.140.60

        “그 중에 님이 Java 쓰고 싶은 함수가 5개면, 그 5개 함수를 호출하는 JNIExport 함수들을 님이 작성해야 합니다.
        그리고 님의 Java 프로그램은 님이 정의한 그 함수들을 호출해서, 결과적으로 C++ SDK에 접금하는 거죠.”

        위의 두 문장을 이해 못하고 있습니다. 왜냐하면, JNIExport 함수의 헤더만 작성하고 바디를 작성하지 않고,
        C++ 로 된 함수를 호출해야 하는데, 위의 예제나 유투브 영상 모두 JNIExport 함수의 바디를 작성하고 있습니다.
        그러면, “결과적으로 C++ SDK 에 접근” -> 어느 함수에 접근하는지 기술하지 않았는데 어느 것을 접근한다는 말인가요?

        위의 std::out 설명을 이해하지만, JNIExport 함수의 예를 보면, hello world 처럼 직접 함수의 바디를 작성하고
        있습니다. SDK 어느 것도 가리키는 것을 보지 못했습니다.

        설명해 주실 수 있다면 정말 감사하겠습니다.

        • rui 64.***.144.10

          자바에 프린트 함수가 없어서 C++에 있는 std::out 함수를 써야 된다고 가정하면, 다음과 같은 함수를 님이 추가한 다음에 자바에서 호출할 수 있겠네요. 이런 식으로 std::out 대신에 다른 SDK의 API도 호출할 수 있죠.

          JNI wrapper:
          JNIEXPORT void JNICALL Java_com_baeldung_jni_ExampleParametersJNI_printMyString
          (JNIEnv* env, jobject thisObject, jstring stringToPrint) {
          std::string myString = env->GetStringUTFChars(stringToPrint, NULL);
          std::cout << myString << std::endl;
          }

          Java function definition:
          private native void printMyString(String stringToPrint);

          • JNI 73.***.140.60

            위에서 보여주신 예문도 유투브에 있는 튜터리얼과 마찬가지로, 자바로 구현하는 것보다 (또는 없어서) C/C++ 로 구현하려고 할 때의 사례입니다. 그렇기 때문에 새롭게 함수를 작성하고 헤더도 JNIEXPORT 를 사용해서 정의하구요.

            제가 질문하는 것은 제3자 DLL 라이브러리 안의 함수를 호출하는 것입니다. 즉, 소스 코드가 없는 DLL 파일과 그 안의 각 함수 헤더만
            알고 있을 때, 그 함수를 자바에서 어떻게 호출하냐는 것이죠.

            가령, 하드웨어를 제어하는 디바이스 드라이버를 DLL 로 제공 받았을 때, 그 안의 함수들을 자바에서 호출하는 방법을 찾고 있었습니다. 하지만, 지금까지 찾아봤지만, 모두 자기가 필요에 의해서 C/C++ 로 새롭게 작성한 함수를 자바에서 호출하는 것만 보여주더군요.

            더 찾아보니, 저와 똑같은 질문을 발견했습니다. 그런데 답변은 JNI 보다는 JNA 를 사용하라는군요. 그게 더 쉽다고.

            https://stackoverflow.com/questions/31778178/using-java-to-access-a-third-party-dll

            http://blog.mwrobel.eu/how-to-call-dll-methods-from-java/

            https://stackoverflow.com/questions/25454697/how-to-call-a-method-in-dll-in-a-java-program

            그러는 와중에 두번째 게시글에 이런 코멘트를 발견했습니다.

            Due to these restrictions of JNI a DLL called from your code must be specifically made for your code. To use an arbitrary DLL from Java you usually have to create an adapting DLL with the conventions of JNI that itself loads the “target” DLL and calls the required functions.

            요점은 자바에서 제3자 라이브러리 함수를 호출하려면 중간 단계를 거치는 방식의 (wrapper 라고 불리는) JNI 방식이 있고,
            중간 단계 없이 직접 호출하는 JNA 라는 방식이 있는 것 같네요.

            아직까지 JNI 를 이용해서 제3자 라이브러리 함수를 (소스 없고 함수 헤더만 제공, 디바이스 드라이버들 처럼) 호출하는 방식은 못찾았습니다.

            더 찾아보겠습니다.