descobrindo profiling de aplicações java com jprofiler - getty/io - diogenes ianakiara

6
artigo Diogenes Buarque Ianakiara ([email protected]. br): Está cursando o último semestre de Análise e Desenvolvimento de Sistemas na FIAP. Trabalha com Java desde 2006. Atualmente é analista de desempenho na Inmetrics. Muitas vezes, nos deparamos com problemas de performance nas aplicações que desenvolvemos ou realizamos manutenções. A maior parte dos problemas está relacionada com a má utilização ou insuficiência de memória. A solução imediata de muitos é aumentar a memória alocada para a aplicação, ou ainda, o número de nós do cluster do servidor de aplicação e finalmente o hardware disponível. Isso tudo sem ao menos realizar uma avaliação prévia do seu comportamento, buscando a causa-raiz dos problemas. Este artigo irá apresentar a técnica de profiling, que visa analisar e identificar problemas de performance em aplicações Java, utilizando a ferramenta JProfiler. 68 www.mundoj.com.br 68 Aprenda como encontrar problemas de performance na sua aplicação com o JProfiler. Descobrindo o Profiling de Aplicações Java com JProfiler J ava Profiling é a técnica que visa analisar o desempenho de aplicações coletando métricas e apresentando em tempo real a porcentagem de utilização de threads, carga de objetos e tempo de resposta dos métodos, possibilitando assim a identificação de gargalos e oportunidades de otimização na aplicação. Neste artigo apresentaremos um exemplo de profiling em uma aplicação remota com a ferramenta JProfiler. Vamos desenvolver um EJB simples com três serviços, que simularão um memory leak e um problema de consumo de cpu. Não abordaremos os conceitos de EJB ou padrões de projetos para não fugir do tema. Assim, iremos configurar o ambiente e o servidor de aplicação para que possamos realizar o profiling. Após a instalação, serão apresentadas as principais funcionalidades da ferra-

Upload: diogenes-buarque-ianakiara

Post on 22-Jan-2018

114 views

Category:

Technology


2 download

TRANSCRIPT

Page 1: Descobrindo profiling de aplicações java com JProfiler - Getty/IO - Diogenes Ianakiara

a r t i g o

Diogenes Buarque Ianakiara

([email protected].

br): Está cursando o último semestre de

Análise e Desenvolvimento de Sistemas

na FIAP. Trabalha com Java desde 2006.

Atualmente é analista de desempenho

na Inmetrics.

Muitas vezes, nos deparamos com problemas de performance nas aplicações

que desenvolvemos ou realizamos manutenções. A maior parte dos problemas

está relacionada com a má utilização ou insuficiência de memória. A solução

imediata de muitos é aumentar a memória alocada para a aplicação, ou

ainda, o número de nós do cluster do servidor de aplicação e finalmente o

hardware disponível. Isso tudo sem ao menos realizar uma avaliação prévia

do seu comportamento, buscando a causa-raiz dos problemas. Este artigo irá

apresentar a técnica de profiling, que visa analisar e identificar problemas

de performance em aplicações Java, utilizando a ferramenta JProfiler.

68 www.mundoj.com.br68

Aprenda como encontrar problemas de performance na sua

aplicação com o JProfiler.

Descobrindo o Profiling deAplicações Java com JProfiler

J ava Profiling é a técnica que visa analisar o desempenho de aplicações coletando métricas e apresentando em tempo real a porcentagem de utilização de threads, carga de objetos e tempo

de resposta dos métodos, possibilitando assim a identificação de gargalos e oportunidades de otimização na aplicação.

Neste artigo apresentaremos um exemplo de profiling em uma aplicação remota com a ferramenta JProfiler. Vamos desenvolver um EJB simples com três serviços, que simularão um memory leak e um problema de consumo de cpu. Não abordaremos os conceitos de EJB ou padrões de projetos para não fugir do tema. Assim, iremos configurar o ambiente e o servidor de aplicação para que possamos realizar o profiling.

Após a instalação, serão apresentadas as principais funcionalidades da ferra-

Page 2: Descobrindo profiling de aplicações java com JProfiler - Getty/IO - Diogenes Ianakiara

69

menta e o exemplo de uso de cada uma delas. O JProfiler irá se conectar remotamente à aplicação desenvolvida e iremos por fim realizar a análise e identificar o problema da aplicação.

O memory leak, ou vazamento de memória, ocorre quando algum componente da nossa aplicação está utilizando uma determinada quantidade de memória para realizar alguma operação e, após finalizar esta operação, este componente não libera a memória utilizada. Geralmente isto ocorre devido a erros de pro-gramação e pode levar ao consumo total da memória.

Instalação e configuração do JProfiler

O JProfiler é uma ferramenta paga, porém a ej-technologies, fornecedora da ferramenta, oferece 10 dias de licença Trial, que até a data de escrita deste artigo está na versão 5.2.3. O download pode ser realizado no site http://www.ej-technologies.com/products/jprofiler/overview.html.

Configuração 1.1

Uma vez terminada a instalação padrão, a primeira execução apresentará um wizard de inicialização rápida, para configurar o seu primeiro profi-ling. Na primeira tela, selecione a opção de profiling em uma aplicação local ou remota “An application Server, locally or remotely”, como mostra a figura 1.

Figura 1. Selecionando o tipo de profiling.

Em seguida, será necessário seguir 11 etapas para configurar o profiling. Na figura 2, é apresentada a primeira etapa, escolha o fornecedor e ver-são do servidor de aplicação. Nesse caso, selecione JBoss 5.x.

Figura 2. Selecionando o servidor de aplicação.

Na segunda etapa, se a aplicação estiver no mesmo servidor em que está instalado o JProfiler, selecione “On this computer”. Caso contrário,

selecione a plataforma remota. No nosso caso, selecionaremos “Linux/AMD64”, como mostra a figura 3.

Na terceira etapa, existem três possibilidades:

connection from the JProfiler GUI”. Com esta opção, o JProfiler con-figura o servidor de aplicação para que ele aguarde o JProfiler se conectar antes de iniciar. Caso o JProfiler não se conecte, o servidor de aplicação não será inicializado).

the JProfiler GUI”, O Jprofiler permite que o servidor inicie normal-mente, possibilitando uma conexão futura com o JProfiler).

cannot connect” possibilita a realização do profiling em modo off-line. Com esta opção, é possível configurar o servidor de aplicação para gravar os dados do profiling em um arquivo e assim possibili-tar uma análise off-line).

No nosso exemplo, vamos selecionar a opção iniciar imediatamente, conforme a figura 4.

Figura 4. Selecionando o tipo de inicialização do Servidor de Aplicação.

Agora defina o endereço do servidor remoto, seguindo o exemplo da figura 5.

Na quinta etapa, o JProfiler deve ser instalado no servidor. Para isto, existe duas maneiras:1 – Next, Next, Finish;

Page 3: Descobrindo profiling de aplicações java com JProfiler - Getty/IO - Diogenes Ianakiara

2 – Copiar os arquivos de uma instalação préexistente para a mesma versão de SO.

O diretório da instalação deve ser informado nessa etapa, como mostra a figura 6.

Na sexta etapa, precisamos informar o diretório onde estará o arquivo de configuração de acesso remoto ao JProfiler, e é de extrema importância para o sucesso do profiling. O nome deste arquivo é config.xml e ele será gerado automaticamente ao final deste wizard no diretório “C:\Docu-ments and Settings\<usuario>\.jprofiler5\config.xml” para a plataforma Windows e “/home/<usuario>/.jprofiler5/” para a plataforma Linux/Unix. Utilize a figura 7 como referência.

É possível observar na figura 8 que agora será necessário acessar o script de inicialização do servidor de aplicação para o JProfiler configurar os pa-râmetros de inicialização necessários. Para esta configuração, compartilhe o diretório do servidor aplicação com as devidas permissões de escrita. Assim, para o JBoss na plataforma Windows acesse o script run.bat no ou run.sh na plataforma Linux/Unix. Após clicar em Next o JProfiler irá gerar um novo script na mesma pasta indicada, chamado run_jprofiler.sh.

Figura 8. Acessando o script de inicializar do servidor.

No próximo passo, devemos informar fornecedor, versão do Java e o modo de compilação, conforme a figura 9.

Como é apresentado na figura 10, o JProfiler utiliza a configuração de-fault, para definir a porta de comunicação. A mesma pode ser alterada caso já esteja em uso.

Aqui finalizamos nossa configuração, podemos visualizar as informações sobre este profiling e todos os cuidados que devemos ter antes de iniciar o profiling, como mostra a figura 11. Na 11ª etapa, você pode escolher entre iniciar imediatamente o profiling ou deixar para depois.

Figura 11. Finalizando a configuração do profiling.

O processo de profiling geralmente é muito oneroso para o processador. Por isso é importante definir um período curto para a execução do profiling. Outro fator importante é o nível de detalhamento do profiling que deve ser definido antes de sua execução, o nível médio é recomendado para CPU e o baixo para memória. Caso seu servidor esteja com folga no uso de memória e CPU, estes níveis podem ser alterados conforme sua necessidade.

70 www.mundoj.com.br

Page 4: Descobrindo profiling de aplicações java com JProfiler - Getty/IO - Diogenes Ianakiara

Para podermos executar o profiling, vamos desenvolver uma miniaplica-ção que simulará o consumo de CPU e o Memory leak. Construiremos um EJB contendo três serviços. O primeiro serviço simulará um vazamento de memória, o segundo simulará o processamento de uma request sim-ples e, por ultimo, teremos um método que terá a uma maior alocação de memória e processamento, entretanto não possuirá vazamento de memória.

Implementação dos serviços 1.1

A Listagem 1 apresenta uma condição de errada no segundo "for", de-vido memory leak que o objeto string tem em cada iteração. Se fosse um registro de um banco de dados, este vazamento poderia resultar em uma falta de resposta da aplicação e esgotar rapidamente da memória disponível.

public void memoryLeak(int iter, int count) {

for (int i=0; i<iter; i++) {

for (int n=0; n<count; n++) {

memoryVector.add(Integer.toString(n+i));

}

for (int n=count-1; n>0; n--) {

memoryVector.removeElementAt(n);

}

}

}

public void noMemoryLeak (int size) {

HashSet tmpStore = new HashSet();

for (int i=0; i<size; ++i) {

String leakingUnit = new String(“Object: “ + i);

tmpStore.add(leakingUnit);

}

}

}

public void requestMemoryLeak(int iter) {

Random requestQueue = new Random();

for (int i=0; i<iter; i++) {

int newRequest = requestQueue.nextInt();

pendingRequests.add(new Integer(newRequest));

}

}

Listagem 1. Implementação do método memoryLeak.

Listagem 3. Implementação do método noMemoryLeak.

Listagem 2. Implementação do método requestMemoryLeak.

A Listagem 2 apresenta um caso muito comum, quando uma requi-sição de entrada é mantida em uma hash table até que a mesma seja concluída, exceto neste exemplo, deixamos de propósito um erro de programação, no qual não removemos do hash table os objetos que foram inseridos anteriormente. Durante um período de tempo, a hash table terá um grande número de alocações, o que resultará em muitas colisões, bem como uma grande parte da pilha ocupada por entradas inúteis. Ambas as listagens são casos muito comuns que resultam em memory leaks.

Podemos pensar que os locais de maior alocação de memória são os dois métodos anteriores. No entanto, isso não é verdade. Por exemplo, na Listagem 3, o método noMemoryLeak aloca uma grande quantidade de memória, porém tudo isso é coletado pelo GC (Garbage Collector), deixando a memória sempre disponível. Por outro lado, os outros dois métodos que não alocam muita memória estão causando constante-mente memory leaks.

Realizando o profiling e identificando o problema

Primeiramente vamos abrir o JProfiler e selecionar a opção “Start Center”. Dentre as opções disponíveis, selecione a opção que você configurou no primeiro tópico, e clique em Start. Conforme a figura 12.

Figura 12. Selecionando a o profiling configurado.

O wizard apresentado na figura 13 dá a possibilidade de configurar os níveis de detalhamento do profiling, iremos utilizar os níveis recomen-dados para não degradar o ambiente testado. Clique em ok para iniciar o profiling.

Agora vamos iniciar nossa aplicação cliente, que por sua vez consumirá os serviços do nosso componente MemoryLeakEJB. A Listagem 4 apre-senta o código que simulará a utilização dos serviços disponíveis. Inicie a aplicação cliente.

7171

Page 5: Descobrindo profiling de aplicações java com JProfiler - Getty/IO - Diogenes Ianakiara

public static void main(String[] args) {

Properties properties = new Properties();

properties.put(“java.naming.factory.initial”,”org.jnp.interfaces.

NamingContextFactory”);

properties.put(“java.naming.factory.url.pkgs”,”=

org.jboss.naming:org.jnp.interfaces”);

properties.put(“java.naming.provider.url”,”10.10.11.97:1099”);

try

{

Context context = new InitialContext(properties);

MemoryLeakRemote memoryLeakRemote =

(MemoryLeakRemote) context.lookup(MemoryLeakBean.

RemoteJNDIName);

for (int i=0; true; i++) {

try {

Thread.sleep(1000);

} catch (InterruptedException e) {

e.printStackTrace();

}

System.out.println(“iteração nº: “ + i);

memoryLeakRemote.slowlyLeakingVector(1000,10);

memoryLeakRemote.leakingRequestLog(5000);

memoryLeakRemote.noLeak(100000);

}

} catch (NamingException e) {

e.printStackTrace();

throw new RuntimeException(e);

}

}

Listagem 4. Implementação de uma aplicação Cliente.

Identificando problemas de vazamento de memória

Neste momento, o JProfiler está conectado com o nosso servidor de aplicação. A primeira métrica que o JProfiler nos apresenta é a “Memory Views”. Estes dados dão a possibilidade de visualizar os níveis de agre-gação, podemos selecionar os níveis Class, Packages, J2EE Components. Para nosso exemplo, selecionaremos J2EE Components. Esta opção apre-senta os componentes JEE mais utilizados no App Server, geralmente ele também apresenta componentes do próprio servidor de aplicação, porém podemos observar na figura 14 que dentre eles encontramos o nosso componente “MemoryLeakBean”.

Figura 14. Visualizando métricas de memória da Aba All Objects.

As abas da parte inferior nos apresentam métricas diversificadas do uso de memória no servidor de aplicação. Ao selecionar uma aba, será apre-

sentado um botão ao qual será possível iniciar o monitoramento que desejamos. Para nosso exemplo, vamos utilizar apenas as aba inicial, All Objects e Allocation Call Tree.

O ideal é avaliar um item por vez, seja ele memória, CPU, threads, ou snapshots. Para não degradar totalmente o ambiente. Uma que você iniciou uma avaliação é impor-tante armazenar todas as evidências do que foi analisado, para futuras comparações, como também para um históri-co do comportamento da aplicação.

Após identificar que o componente MemoryLeakBean está na lista de objetos da aba All Objects, selecione a aba Allocation Call Tree. Esta aba apresentará uma árvore de utilização da memória.

Como podemos observar na imagem da figura 15, os métodos do componente MemoryLeakBean consomem aproximadamente 50% do tempo de uso e alocação de memória, especialmente o método noLeak.

Agora vamos selecionar a opção VM Telemetry Views. Esta opção apresen-tará gráficos de utilização da Heap, Thread e até CPU load. Ao selecionar pela primeira vez esta opção, já podemos observar um comportamento incomum da Heap. Notamos na figura 16 que este comportamento re-presenta um memory leak ou vazamento de memória.

Indo mais a fundo, vamos selecionar a aba GC Activity, que nos apresen-tará um gráfico da atividade do Garbage Collector. Podemos notar na fi-gura 17 uma intensa atividade de GC, este comportamento se dá quando possuímos muitos novos objetos alocados, preenchendo todo espaço da Heap. Caso não haja mais memória para alocação, será lançado o “velho e conhecido” erro java.lang.OutOfMemory.

72 www.mundoj.com.br

Page 6: Descobrindo profiling de aplicações java com JProfiler - Getty/IO - Diogenes Ianakiara

Figura 17. Visualizando o gráfico de atividade do GC.

Identificando problemas de consumo de CPU

A opção CPU Views apresenta detalhadamente a árvore de uso de CPU. Podemos observar na figura 18 que 95% da utilização de CPU está alo-cada nos três métodos que estamos consumindo. Podemos filtrar ainda mais utilizando a opção Thread status. Esta opção fornece a visualização detalhada do uso de classes e métodos por estado de cada thread, seja ele blocked, running ou waiting.

Figura 18. Visualizando a árvore de consumo de CPU.

As abas Hot Spots apresentam tanto para memória ou CPU pontos que possivelmente podem ser críticos, porém estas métricas requerem um maior nível de detalhamento do profiling. Muitas vezes estas métricas podem apresentar resultados genéricos, como java.lang.String ou java.lang.Integer, entretanto, se você possuir um método muito oneroso para o sistema, ele com certeza aparecerá nesta análise. Como podemos ob-servar o método noLeak da figura 19.

Figura 19. Visualizando possíveis pontos de interesse.

Após identificar as causas de um problema de performance, deve-se ana-lisar profundamente o problema para se obter a melhor solução. Muitas vezes estes problemas podem ser resolvidos com otimizações do código, porém nem sempre podemos alterar o mesmo, por exemplo, quando o código não nos pertence. Outros problemas podem ser resolvidos com o tuning da JVM.

Tuning da JVM é o ato de otimizar o uso do garbage collector para que ele seja executado de forma mais efi-ciente, possibilitando assim um ganho de performance na aplicação.

Não está no escopo do artigo se aprofundar em como resolver os pro-blemas de performance ou como desenvolver um EJB. Todos os códigos estarão disponíveis no site http://www.mundoj.com.br.

Sab

er m

ais Na edição 35 da revista Mundoj o artigo “Co-

nhecendo os parâmetros de configuração mais utilizados da JVM” apresentou técnicas de como otimizar a JVM.

Considerações finais

Neste artigo foi apresentadas algumas formas de identificar problemas de performance em aplicações Java utilizando a técnica de profiling. Esta técnica está fortemente ligada ao ciclo de vida das aplicações, uma vez que uma aplicação já nasce com problemas de performance, certamente estes problemas só irão aumentar. Assim buscamos apresentar não só os conceitos do profiling, mas também um exemplo prático de como iden-tificar esses problemas.

No primeiro tópico apresentamos como configurar a ferramenta e cons-truir um profiler remoto, seguindo, construímos duas aplicações, um EJB que simulou problemas de memory leak e cpu load e uma aplica-ção cliente que por sua vez consumiu todos os serviços do EJB. Assim, aprendemos como iniciar um profiling visualizando métricas de memória e cpu e como identificar não só o componente, mas também a classe e os métodos que apresentavam os problemas de performance. É importante salientar que apenas identificando o gargalo não necessariamente o problema estará resolvido. É preciso analisar e evidenciar para se chegar

Referências

7373