Você está na página 1de 7

JAVA VIRTUAL MACHINE (JVM)

Por Leandro Baptista, Marlon Palangani e Tiago Deoldoto, 11 de Abril de 2009

A linguagem de programação Java proporciona o desenvolvimento aplicações que podem ser


executadas em diversos tipos de plataformas e Sistemas Operacionais. Tudo isso tornou-se
possível graças a Máquina Virtual Java. Nesse artigo você encontrará informações a respeito do
funcionamento, arquitetura e diversas vantagens que essa ferramenta proporciona, tanto para
usuários simples como para desenvolvedores de sistemas.

O QUE É JAVA VIRTUAL MACHINE?

A JVM (do inglês Java Virtual Machine) é um programa que carrega e executa os aplicativos Java,
convertendo os bytecodes em código executável de máquina, é apenas um aspecto do software Java
envolvido na interação com a Web. A JVM é reponsável pelo gerenciamento dos aplicativos, à medida que
são executados.

Graças à maquina virtual Java, os programas escritos em Java podem funcionar em qualquer plataforma de
hardware e software que possua uma versão da JVM, tornando assim essas aplicações independentes da
plataforma onde funcionam.

A arquitetura de JVM permite um controle muito fino sobre as liberadas para o código que esta rodando na
VM. Isso permite a execução de código confiável de fontes remotas, um modelo usado pelos applets. Os
applets rodam dentro de uma VM incorporada ao browser do usuário, exectando código baixado de um
servidor HTTP remoto. O código remoto roda em uma sandbox, que protege o usuário de códigos maliciosos.
O autor do applet pode aplicarum certificado para assinar digitalmente o applet como “seguro”, dando a ele
permissão de sair do sandbox e acessar livremente a máquina onde esta rodando.

Podemos dizer também que a maquina virtual Java é uma máquina de computação abstrata. Como uma
máquina de computação real, tem um jogo de instrução e manipula várias áreas de memória no tempo de
execução. É razoavelmente comum executar uma linguagem de programação usando máquina virtual. A
máquina virtual mais conhecida, pode ser a máquina do P-Código de UCSD Pascal.

A primeira execução do protótipo da máquina virtual de Java, emulou o jogo de instruções em um software
hospedado por um dispositivo handheld que se assemelhasse a um assistente de Digitas pessoais
contemporâneo (PDA). As execuções atuais da máquina virtual de Java, emulam em anfitriões de Win32 e de
solaris em umas maneiras muito mais sofisticadas. Com a linguagem de programação Java, podemos, por
exemplo, criar um aplicativo que rode tanto no Linux quanto no Windows, como foi dito acima. Mas não se
limita a esses sistemas operacionais, sendo possivel desenvolver para uma infinidade de plataformas, para
isso basta que elas tenham uma JVM.
Você ja deve ter usado Java antes e não sabe. Por exemplo, em uma fila de banco, onde você fica jogando
em seu telefone celular enquanto aguarda sua vez. Os aplicativos feitos em Java estão presentes em uma
infinidade de dispositivos, desde relógios até mainframes, relembrando novamente que, tudo isso graças a
Máquina Virtual Machine.

ENTENDENDO MELHOR O FUNCIONAMENTO DA JVM

Vamos tentar entender melhor como a JVM funciona. Quando se faz um programa em Java e o compila, se
tudo estiver certo, o compilador gera bytecodes desse programa. Bytecode é uma espécie de codificação que
traduz tudo o que foi escrito no programa para um formato que a JVM entenda e seja capaz de executar,
assim sendo capaz de rodar em qualquer sistema operacional que tenha a JVM. Isso ocorre porque não
existe bytecodes diferentes, isto é, os bytecodes dos programas em Java compilados no Windows são
constituiídos da mesma forma que bytecodes gerados se a compilação fosse feita em Mac OS. De certo que,
podem haver algumas diferenças, que dependem da implementação da JVM e claro, do compilador. Quando
um código em Java é compilado, um arquivo com a extensão .class é gerado. Esse tipo de arquivo é o
bytecode, então quando o programa Hello.java for compilado, um arquivo chamado Hello.class deverá ser
executado. A imagem a seguir ilustra esse processo.

Construir uma JVM não é fácil, ela envolve uma série de conceitos complexos, como instruções equivalentes
a de processadores, controle de acesso à memória, registradores, etc. Isso pode até parecer estranho, mas é
necessário entender que as JVMs atuam, de fato, como uma máquina.

Muitos usuários relutam em instalar a máquina virtual Java, dizem que gastam memória, que ocupa muito
espaço desnecessário, mas a verdade é que bem configurado, ela se torna leve “como uma pluma”.

Uma curiosidade que podemos citar, é que, um ponto que merece destaque na arquitetura desenvolvida pela
Sun para a plataforma Java é o uso da máquina virtual em troca de um código compilado especifico para um
computador. Diversos outros modelos necessitam que um determinado programa seja compilado para cada
tipo de computador em que será utilizado, o que pode ser proibitivo para alcançar uma larga escala.

ARQUITETURA DA MAQUINA VIRTUAL JAVA

Principais subsistemas da máquina virtual Java (JVM):

- Carregador de classe (class loader), carregar classes e interfaces a partir de nomes completamente
qualificados.

- Máquina de execução (execution engine), executa instruções das classes carregadas.

- Áreas de dados de execução (runtime data areas), organizada em área de métodos (method area), área
de memória dinâmica (help), pilhas (stacks), contadores de programas (program counters ou pc) e pilhas dos
métodos nativos (native methods stacks). A especificação destas áreas varia de implementação para
implementação para permitir que características especificas de uma dada arquitetura possam ser exploradas.
Cada instância da máquina virtual tem uma área de métodos e uma “heap” que são compartilhadas por todas
as “threads” sendo executadas na máquina virtual. Cada “thread” cria um registro de ativação (frame) na pilha
da “thread” contendo o estado do método, o que inclui parâmetros, variáveis locais, valor de retorno e cálculos
intermediários.

- Interface de métodos nativos (native method interface).

A JVM é uma maquina de pilha. As instruções da JVM utilizam a pilha para armazenar resultados
intermediários, ao invés de utilizar registradores como é feito em arquiteturas concretas. Isto permite a
definição de um conjunto simples de instruções que é facilmente implementado em diferentes arquiteturas.

Na JVM existem tipos primitivos, como byte, short, int, long, float e Double e referencias a objetos. O tamanho
de uma palavra (“Word”) na JVM varia de implementação para implementação da JVM e deve ser grande o
suficiente para armazenar um byte, int, float, uma referencia a um objeto ou um “return address”, este ultimo
utilizado para implementar cláusulas finally em Java. Duas palavras devem ser capazes de armazenar os
tipos long e Double.

OTIMIZANDO O USO DA MEMÓRIA EM APLICAÇÕES JAVA

Em Java, diferente de outras linguagens como C e C++, o programador não precisa desalocar memória,
usando funções como free e delete. Java possui um coletor de lixo, uma thread de baixa prioridade que
executa junto com a Java Virtual Machine. Sua função é gerenciar a utilização de memória, procuranado
áreas de memória que não estejam mais em uso para realizar a liberação das mesmas.
A execução do coletor de lixo é transparente para o programador, porém, podemos em algumas situações
desejar usar artifícios para otimizar o uso da memória pela JVM.

FUNCIONAMENTO DO COLETOR DE LIXO

O coletor de lixo libera a memória mantida por um objeto se não houver mais nenhuma referência para ele.
Dependendo do algoritmo da coleta de lixo usado pela máquina virtual Java no qual o código está sendo
executado, nem todo objeto não referenciado será desalocado pelo coletor de lixo. Um objeto mais antigo, de
longa duração, é menos provável de estar não referenciado do que um objeto novo, de modo que um
algoritmo comum para coleta de lixo é analisar os objetos mais antigos com menos freqüência do que os
objetos mais novos.
Podemos ter uma métrica da eficiência da implementação do coletor de lixo utilizado pela sua JVM com o
código abaixo:
Runtime rt = Runtime.getRuntime();
long mem = rt.freeMemory();
System.out.println("Memória Livre: " + mem);
// ... algum código
System.gc();
// ...
mem = rt.freeMemory();
System.out.println("Memória Livre: " + mem);

O código acima obtém o ambiente de execução com o método estático getRuntime() da classe Runtime. O
método freeMemory() retorna a memória disponível no ambiente runtime. A linha System.gc() faz a
chamada explícita do coletor de lixo. Em algumas implementações da JVM, o coletor de lixo pode ser ativado
através da chamada ao método System.gc(), embora a especificação não garanta isso. De todo modo, a
maior parte das JVMs fazem a chamada do coletor implicitamente quando a memória está baixa ou quando a
CPU está inativa por um certo período de tempo.
Problemas com a utilização de memória podem surgir quando os objetos contém variáveis de instância que
são iniciadas no construtor, ocupam um grande quantidade de memória e não são mais utilizadas no restante
da aplicação. Isso pode degradar o desempenho de um código.

LIBERANDO UM OBJETO

Uma maneira de tornar um objeto candidato a ser retirado da memória pelo coletor de lixo é atribuindo o valor
null à sua referência. Com isso, o objeto torna-se não referenciado e pode ser retirado da memória pelo
coletor de lixo. O programa abaixo ilustra essa técnica:

import java.util.*;

class GarbageExample {

private static Vector vetor;

public static void main(String args) {

vetor = new Vector();


for (int a=0; a < 500; a++)
vetor.addElement(new StringBuffer("teste"));

Runtime rt = Runtime.getRuntime();

System.out.println("Memória Livre: " + rt.freeMemory());

vetor = null;

System.gc();

System.out.println("Memória Livre: " + rt.freeMemory());

}
}
O código acima aloca 500 objetos StringBuffer e os coloca dentro de um Vector. O objeto da classe Vector é
referenciado pela variável vetor. Quando se atribui o valor null para a variável vetor o conteúdo do objeto
Vector (500 objetos StringBuffer) não possui mais referência na aplicação. A chamada explícita ao coletor de
lixo faz a limpeza desse objeto. O programa exibe a quantidade de memória livre antes e depois da execução
do coletor de lixo.
Essa técnica pode solucionar o problema de manter grandes quantidades de memória desnecessárias. Para
usar o mínimo de memória possível, os objetos que precisem existir durante um tempo maior devem ser os
menores possíveis. Do outro lado, os grandes objetos deverão existir pelo menor tempo possível.
Essa técnica pode ajudar, em alguns casos, somente o desempenho das aplicações Java que executam na
mesma JVM e não necessariamente de outros processos rodando no sistema. Isso porque muitos algoritmos
de gerenciamento de heap alocam antecipadamente uma parte da memória para ser usada pelo seu código.
Ou seja, você libera a memória da máquina virtual Java, o que não afetará outras aplicações não-Java, pelo
fato da JVM pre-alocar o heap que ela precisa.
A qualquer momento pode-se solicitar a execução do coletor de lixo (sem garantias de execução), chamando
o método System.gc(). Porém, é necessário analisar o impacto de desempenho na sua aplicação. Muitos
algoritmos de coleta de lixo suspendem todas as outros threads antes de entrar em execução. Isso garante
que, quando o coletor de lixo for executado, ele terá acesso completo à memória no heap e poderá realizar
suas tarefas com segurança, sem que seja interrompido por outra thread. Quando o coletor de lixo termina
seu trabalho, todas as threads que ele suspendeu são retomados.
O coletor de lixo de Java é executado com uma grande freqüência, sua chamada explícita dificilmente é
necessária. Porém, em alguns casos pode tornar-se conveniente. É recomendado evitar a chamada ao
método System.gc() pelo bloqueio que provoca. É recomendado também nunca fazer essa chamada
explícita dentro de loops.

O MÉTODO FINALIZE()
O programador pode definir o método finalize() que será chamado sempre antes do objeto ser retirado da
memória pelo coletor de lixo. A aplicação abaixo ilustra isso:
import java.util.*;

class GarbageExample {

private static MeuVetor vetor;


public static void main(String args) {

vetor = new MeuVetor();

for (int a=0; a <500; a++)


vetor.addElement(new StringBuffer("teste"));
Runtime rt = Runtime.getRuntime();

System.out.println("Memória Livre: " + rt.freeMemory());

vetor = null; // deixa os 500 StringBuffers sem //referência

System.gc();

System.out.println("Memória Livre: " + rt.freeMemory());


}
}

class MeuVetor extends Vector {


public void finalize() {
System.out.println("Vou ser coletado!!");
}
}
O método finalize da classe MeuVetor é executado antes do objeto ser coletado da memória, a saída deste
programa é:
Memória Livre: 459808
Vou ser coletado!!
Memória Livre: 600664

JVM E QUESTÃO DA SEGURANÇA

Desde sua criação, sempre considerou com seriedade a questão da segurança. Por isso, é praticamente
impossível criar programas em Java para fins maliciosos. Quando um programa em Java é executado, seu
bytecode precisa passar pelos requisitos de segurança presentes na JVM, que impede a execução se o
código tiver alguma irregularidade. Assim, se no programa houver instruções para acessar áreas restritas da
memória ou acessar recursos de hardware, a JVM não aprovará o código.

Outras linguagens, como C, são executadas diretamente pelo sistema operacional. Com isso, é possível criar
programas que acessem recursos críticos do sistema. No caso da linguagem Java, a JVM atua como uma
espécie de intermediária entre o programa e o sistema. Sendo assim, até mesmo o acesso a recursos de
entrada e saída só é feito por meio da JVM.

UM EXEMPLO ATUAL DA UTILIZAÇÃO DO JVM

ST lança Máquina Virtual Java para TV Interativa.

A STMicroelectronic anunciou a disponibilidade do STJ Engine, uma arquitetura de software projetada para
funcionar com a Máquina Virtual Java (JVM).

O STJ Engine oferece uma Java Virtual Machine (JVM) otimizada com compilador HotSpot Just-In-Time (JIT)
e implementa a interfaces padrões como Java debugger (JVMDI) e Java profiling (JVMPI), permitindo
debugging e profiling de aplicações Java.

Ao utilizar essas capacidades, os clientes poderão aprimorar as capacidades gráficas de seus sistemas,
permitindo uma interface de usuários muito melhor e recursos interativos como jogos de perguntas e
respostas, compra virtual, e outros.
REFERÊNCIA BIBLIOGRÁFICA

VENNERS, Bill. Inside the Virtual Machine.


http://www.mundojava.com.br
http://forums.sun.com/index.jspa
http://www.ime.usp.br
http://www.infowester.com
http://www.elektorbrasil.com.br