Atenção: esse artigo tem um vídeo complementar. Clique e assista!

Artigo no estilo: Curso

De que se trata o artigo:

O artigo trata sobre a integração com código nativo adotando a JNI. Para estudar o tema veremos na prática como criar uma aplicação C/C++ e Java. Além disso, também examinaremos os custos de desempenho e outros aspectos do acesso ao código nativo.


Para que serve:

Com o avanço do desempenho das JVMs, hoje em dia é raro precisarmos de código nativo por motivo de desempenho. Mas isso ainda acontece, embora a decisão já não seja tão simples. Neste artigo, abordaremos a JNI como solução para isso.


Em que situação o tema é útil:

É muito comum nos depararmos com situações para as quais não estamos totalmente preparados. Talvez você nunca seja requisitado para desenvolver sistemas que utilizem código C/C++, mas caso isso aconteça, você terá um grande diferencial se estiver capacitado.

Na primeira parte deste minicurso, publicada na Edição 85, começamos a preparar o ambiente de desenvolvimento a ser utilizado na integração entre código nativo e Java. A partir de agora, vamos trabalhar com a Java Native Interface, ou JNI, para demonstrar a criação de um projeto “misto”. Em seguida realizaremos alguns testes e analisaremos o desempenho do nosso código.

Trabalhando com JNI

A programação com JNI já foi coberta pela Java Magazine, no artigo “Acesso a Código Nativo com JNI” de André Dantas Rocha (Edição 36). Apesar de esta edição datar de quatro anos atrás, não vamos repetir uma exploração aprofundada da JNI, pois existe uma alternativa mais fácil e moderna que quero explorar (seção seguinte sobre JNA). Mas também não podemos ignorar a JNI, que sempre será o fundamento de qualquer atividade de integração entre Java e código nativo.

Vamos criar um projeto “misto”, com uma parte em C e outra em Java, para ilustrar o uso da JNI. Neste projeto, teremos um algoritmo de ordenação de arrays, implementado em C presumivelmente por motivo de desempenho. O programa Java irá criar um array com dados desordenados, invocar o método nativo de ordenação, e de volta ao Java, validar os resultados e exibir o tempo de execução. (E como eu não resisto a benchmark ou comparações entre linguagens, faremos também uma versão puro-Java da ordenação e validaremos a diferença real de desempenho...)

Curiosamente, nenhum dos IDEs – CDT ou NetBeans C/C++ – possui suporte especial para desenvolvimento JNI; não há funções que nos livrem de tarefas como a execução manual do utilitário javah. Isso parece estranho, pois é um cenário de uso que deve ser relativamente comum em IDEs que suportam tanto Java quanto C/C++! Mas é melhor assim, pois daqui por diante vamos andar com nossos próprios pés, ao invés de apenas clicar botões de assistentes de IDEs.

Criando o programa Java

Não existem projetos “mistos” Java & C/C++; precisaremos de dois projetos independentes. Comece então criando um projeto Java SE comum. Neste projeto, adicione as classes da Listagem 1. A novidade está no método TestJNI.sortJNI(), declarado com o modificador native. Isso parece um método abstrato (não tem corpo), mas não é: o método é concreto, só não é implementado em Java.

Listagem 1. Programa Java que invoca código nativo.


  Main.java
    import java.util.Arrays;
  import java.util.Random;
   
  public class Main {
    public static void main (String[] args) {
      int[] dados = criaDados(4 * 1024*1024);
      
      System.out.println("Java...");
      for (int i = 0; i < 5; ++i)
        TestJava.bench(Arrays.copyOf(dados, dados.length));
      
      System.out.println("JNI...");
      for (int i = 0; i < 5; ++i)
        TestJNI.bench(Arrays.copyOf(dados, dados.length));
    }
    
    private static int[] criaDados (int tamanho) {
      int[] dados = new int[tamanho];
      Random rnd = new Random();
      for (int i = 0; i < dados.length; ++i)
        dados[i] = rnd.nextInt();
      return dados;
    }
  }
   
  
  TestJava.java
    import java.util.Arrays;
   
  class TestJava {
    static void bench (int[] dados) {
      long time = System.nanoTime();
      for (int i = 0; i < Main.R; ++i)
      Arrays.sort(dados);
      time = System.nanoTime() - time;
      System.out.println(time / 1e6);
    }
  }
   
  
  TestJNI.java
    class TestJNI {
    static {
      System.loadLibrary("SortJNI");
    }
    private static native void sortJNI (int[] dados);
    
    static void bench (int[] dados) {
      long time = System.nanoTime();
      for (int i = 0; i < Main.R; ++i)
      sortJNI(dados);
      time = System.nanoTime() - time;
      System.out.println(time / 1e6);
    }
  }  ... 

Quer ler esse conteúdo completo? Tenha acesso completo