Что такое нативные методы их плюсы и минусы java
Перейти к содержимому

Что такое нативные методы их плюсы и минусы java

  • автор:

W hat is a Native method?

Suvasish Das

Sometimes we want to call some subroutine that is written in another language than java. This is done by the java native method. A subroutine that exists as executable code for the CPU and environment that you are working in is known as the Native Method.

Native Method in Java

Java uses “native” keyword to declare a native method. example —

most of the native methods are written in “C” language for its speed. The mechanism used to integrate C code within the java program is known as “Java Native Interface (JNI)”.

to understand the java native program, go through my example below.

STEP 1:

Filename: NativeDemo.java

In this above class, the test() method is declared as the native method and we want to write “C” implementation for the method.

Note that the static block, static block will execute only once when the java program begins execution. The library is loaded by the java LoadLibrary() method, which is a part of the System class.

after writing the code, compile it to produce NativeDemo.class.

STEP 2:

Now we have a .java file and its corresponding .class file. In this step, we want to produce a .h file (C header file) to write the C implementation. To do this use javah which is for providing headers, run the following command.

after running this command, the compiler will produce a NativeDemo.h header. looks like this —

Filename: NativeDemo.h

In this file, we have JNIEXPORT void JNICALL Java_NativeDemo_test(JNIEnv *, jobject); which is the corresponding native method.

STEP 3:

In this step, we are going to write the NativeDemo.c file for NativeDemo.h

Filename: NativeDemo.c

STEP 4:

Now it’s time to compile the NativeDemo.c file and produce the corresponding .dll file. To do this, run the command-

the header_path is the location of the headers where jni.h and jni_md.h located. In my case, the locations are ‘/usr/lib/jvm/java-1.14.0-openjdk-amd64/include’ and ‘/usr/lib/jvm/java-1.14.0-openjdk-amd64/include/linux’

for more about include path during compile time click here.

now we are ready to produce .dll file. run the command bellow-

Родное ключевое слово и методы Java

В этом кратком руководстве мы обсудим концепцию нативного ключевого слова в Java, а также покажем, как интегрировать нативные методы в код Java.

2. Родное ключевое слово в Java​

Прежде всего, давайте обсудим, что такое нативное ключевое слово в Java.

Проще говоря, это модификатор без доступа, который используется для доступа к методам, реализованным на языке, отличном от Java, таком как C/C++ .

Он указывает зависящую от платформы реализацию метода или кода, а также действует как интерфейс между JNI и другими языками программирования.

3. Родные методы​

Собственный метод — это метод Java (либо метод экземпляра, либо метод класса), реализация которого также написана на другом языке программирования, таком как C/C++.

Более того, метод, помеченный как нативный , не может иметь тела и должен заканчиваться точкой с запятой:

Мы можем использовать их для:

  • реализовать интерфейс с системными вызовами или библиотеками, написанными на других языках программирования
  • получить доступ к системным или аппаратным ресурсам, которые доступны только из другого языка
  • интегрировать уже существующий устаревший код, написанный на C/C++, в приложение Java
  • вызвать скомпилированную динамически загружаемую библиотеку с произвольным кодом из Java

4. Примеры​

Давайте теперь продемонстрируем, как интегрировать эти методы в наш код Java.

4.1. Доступ к собственному коду в Java​

Прежде всего, давайте создадим класс DateTimeUtils , которому необходимо получить доступ к платформенно-зависимому собственному методу с именем getSystemTime :

Чтобы загрузить его, мы будем использовать System.loadLibrary.

Давайте поместим вызов для загрузки этой библиотеки в статический блок, чтобы она была доступна в нашем классе:

Мы создали библиотеку динамической компоновки, nativedatetimeutils , которая реализует getSystemTime на C++, используя подробные инструкции, приведенные в нашем руководстве по JNI .

4.2. Тестирование нативных методов​

Наконец, давайте посмотрим, как мы можем протестировать нативные методы, определенные в классе DateTimeUtils :

Guide to JNI (Java Native Interface)

Building or modernizing a Java enterprise web app has always been a long process, historically. Not even remotely quick.

That’s the main goal of Jmix is to make the process quick without losing flexibility — with the open-source RAD platform enabling fast development of business applications.

Critically, it has very minimal impact on your server’s performance, with most of the profiling work done separately — so it needs no server changes, agents or separate services.

Simply put, a single Java or Kotlin developer can now quickly implement an entire modular feature, from DB schema, data model, fine-grained access control, business logic, BPM, all the way to the UI.

Jmix supports both developer experiences – visual tools and coding, and a host of super useful plugins as well:

Slow MySQL query performance is all too common. Of course it is. A good way to go is, naturally, a dedicated profiler that actually understands the ins and outs of MySQL.

The Jet Profiler was built for MySQL only, so it can do things like real-time query performance, focus on most used tables or most frequent queries, quickly identify performance issues and basically help you optimize your queries.

Critically, it has very minimal impact on your server’s performance, with most of the profiling work done separately — so it needs no server changes, agents or separate services.

Basically, you install the desktop application, connect to your MySQL server, hit the record button, and you’ll have results within minutes:

The Kubernetes ecosystem is huge and quite complex, so it’s easy to forget about costs when trying out all of the exciting tools.

To avoid overspending on your Kubernetes cluster, definitely have a look at the free K8s cost monitoring tool from the automation platform CAST AI. You can view your costs in real time, allocate them, calculate burn rates for projects, spot anomalies or spikes, and get insightful reports you can share with your team.

Connect your cluster and start monitoring your K8s costs right away:

Get started with Spring 5 and Spring Boot 2, through the Learn Spring course:

> CHECK OUT THE COURSE

1. Introduction

As we know, one of the main strengths of Java is its portability – meaning that once we write and compile code, the result of this process is platform-independent bytecode.

Simply put, this can run on any machine or device capable of running a Java Virtual Machine, and it will work as seamlessly as we could expect.

However, sometimes we do actually need to use code that’s natively-compiled for a specific architecture.

There could be some reasons for needing to use native code:

  • The need to handle some hardware
  • Performance improvement for a very demanding process
  • An existing library that we want to reuse instead of rewriting it in Java.

To achieve this, the JDK introduces a bridge between the bytecode running in our JVM and the native code (usually written in C or C++).

The tool is called Java Native Interface. In this article, we’ll see how it is to write some code with it.

2. How It Works

2.1. Native Methods: the JVM Meets Compiled Code

Java provides the native keyword that’s used to indicate that the method implementation will be provided by a native code.

Normally, when making a native executable program, we can choose to use static or shared libs:

  • Static libs – all library binaries will be included as part of our executable during the linking process. Thus, we won’t need the libs anymore, but it’ll increase the size of our executable file.
  • Shared libs – the final executable only has references to the libs, not the code itself. It requires that the environment in which we run our executable has access to all the files of the libs used by our program.

The latter is what makes sense for JNI as we can’t mix bytecode and natively compiled code into the same binary file.

Therefore, our shared lib will keep the native code separately within its .so/.dll/.dylib file (depending on which Operating System we’re using) instead of being part of our classes.

The native keyword transforms our method into a sort of abstract method:

With the main difference that instead of being implemented by another Java class, it will be implemented in a separated native shared library.

A table with pointers in memory to the implementation of all of our native methods will be constructed so they can be called from our Java code.

2.2. Components Needed

Here’s a brief description of the key components that we need to take into account. We’ll explain them further later in this article

  • Java Code – our classes. They will include at least one native method.
  • Native Code – the actual logic of our native methods, usually coded in C or C++.
  • JNI header file – this header file for C/C++ (include/jni.h into the JDK directory) includes all definitions of JNI elements that we may use into our native programs.
  • C/C++ Compiler – we can choose between GCC, Clang, Visual Studio, or any other we like as far as it’s able to generate a native shared library for our platform.

2.3. JNI Elements in Code (Java And C/C++)

  • “native” keyword – as we’ve already covered, any method marked as native must be implemented in a native, shared lib.
  • System.loadLibrary(String libname) – a static method that loads a shared library from the file system into memory and makes its exported functions available for our Java code.

C/C++ elements (many of them defined within jni.h)

  • JNIEXPORT- marks the function into the shared lib as exportable so it will be included in the function table, and thus JNI can find it
  • JNICALL – combined with JNIEXPORT, it ensures that our methods are available for the JNI framework
  • JNIEnv – a structure containing methods that we can use our native code to access Java elements
  • JavaVM – a structure that lets us manipulate a running JVM (or even start a new one) adding threads to it, destroying it, etc…

3. Hello World JNI

Next, let’s look at how JNI works in practice.

In this tutorial, we’ll use C++ as the native language and G++ as compiler and linker.

We can use any other compiler of our preference, but here’s how to install G++ on Ubuntu, Windows, and MacOS:

  • Ubuntu Linux – run command “sudo apt-get install build-essential” in a terminal
  • Windows – Install MinGW
  • MacOS – run command “g++” in a terminal and if it’s not yet present, it will install it.

3.1. Creating the Java Class

Let’s start creating our first JNI program by implementing a classic “Hello World”.

To begin, we create the following Java class that includes the native method that will perform the work:

As we can see, we load the shared library in a static block. This ensures that it will be ready when we need it and from wherever we need it.

Alternatively, in this trivial program, we could instead load the library just before calling our native method because we’re not using the native library anywhere else.

3.2. Implementing a Method in C++

Now, we need to create the implementation of our native method in C++.

Within C++ the definition and the implementation are usually stored in .h and .cpp files respectively.

First, to create the definition of the method, we have to use the -h flag of the Java compiler. It should be noted that for versions earlier than java 9, we should use the javah tool instead of javac -h command:

This will generate a com_baeldung_jni_HelloWorldJNI.h file with all the native methods included in the class passed as a parameter, in this case, only one:

As we can see, the function name is automatically generated using the fully qualified package, class and method name.

Also, something interesting that we can notice is that we’re getting two parameters passed to our function; a pointer to the current JNIEnv; and also the Java object that the method is attached to, the instance of our HelloWorldJNI class.

Now, we have to create a new .cpp file for the implementation of the sayHello function. This is where we’ll perform actions that print “Hello World” to console.

We’ll name our .cpp file with the same name as the .h one containing the header and add this code to implement the native function:

3.3. Compiling and Linking

At this point, we have all parts we need in place and have a connection between them.

We need to build our shared library from the C++ code and run it!

To do so, we have to use G++ compiler, not forgetting to include the JNI headers from our Java JDK installation.

Once we have the code compiled for our platform into the file com_baeldung_jni_HelloWorldJNI.o, we have to include it in a new shared library. Whatever we decide to name it is the argument passed into the method System.loadLibrary.

We named ours “native”, and we’ll load it when running our Java code.

The G++ linker then links the C++ object files into our bridged library.

We can now run our program from the command line.

However, we need to add the full path to the directory containing the library we’ve just generated. This way Java will know where to look for our native libs:

4. Using Advanced JNI Features

Saying hello is nice but not very useful. Usually, we would like to exchange data between Java and C++ code and manage this data in our program.

4.1. Adding Parameters To Our Native Methods

We’ll add some parameters to our native methods. Let’s create a new class called ExampleParametersJNI with two native methods using parameters and returns of different types:

And then, repeat the procedure to create a new .h file with “javac -h” as we did before.

Now create the corresponding .cpp file with the implementation of the new C++ method:

We’ve used the pointer *env of type JNIEnv to access the methods provided by the JNI environment instance.

JNIEnv allows us, in this case, to pass Java Strings into our C++ code and back out without worrying about the implementation.

We can check the equivalence of Java types and C JNI types into Oracle official documentation.

To test our code, we’ve to repeat all the compilation steps of the previous HelloWorld example.

4.2. Using Objects and Calling Java Methods From Native Code

In this last example, we’re going to see how we can manipulate Java objects into our native C++ code.

We’ll start creating a new class UserData that we’ll use to store some user info:

Then, we’ll create another Java class called ExampleObjectsJNI with some native methods with which we’ll manage objects of type UserData:

One more time, let’s create the .h header and then the C++ implementation of our native methods on a new .cpp file:

Again, we’re using the JNIEnv *env pointer to access the needed classes, objects, fields and methods from the running JVM.

Normally, we just need to provide the full class name to access a Java class, or the correct method name and signature to access an object method.

We’re even creating an instance of the class com.baeldung.jni.UserData in our native code. Once we have the instance, we can manipulate all its properties and methods in a way similar to Java reflection.

We can check all other methods of JNIEnv into the Oracle official documentation.

4. Disadvantages of Using JNI

JNI bridging does have its pitfalls.

The main downside being the dependency on the underlying platform; we essentially lose the “write once, run anywhere” feature of Java. This means that we’ll have to build a new lib for each new combination of platform and architecture we want to support. Imagine the impact that this could have on the build process if we supported Windows, Linux, Android, MacOS…

JNI not only adds a layer of complexity to our program. It also adds a costly layer of communication between the code running into the JVM and our native code: we need to convert the data exchanged in both ways between Java and C++ in a marshaling/unmarshaling process.

Sometimes there isn’t even a direct conversion between types so we’ll have to write our equivalent.

5. Conclusion

Compiling the code for a specific platform (usually) makes it faster than running bytecode.

This makes it useful when we need to speed up a demanding process. Also, when we don’t have other alternatives such as when we need to use a library that manages a device.

However, this comes at a price as we’ll have to maintain additional code for each different platform we support.

That’s why it’s usually a good idea to only use JNI in the cases where there’s no Java alternative.

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *