e
aperfeiçoamentos. Estes são detalhados no documento J2SE 1.5 “Tiger” Feature List,
cujo primeiro release público foi aprovado há pouco pelo JCP. É, porém,
uma especificação dirigida aos implementadores da J2SE e não aos
usuários finais – os desenvolvedores Java. A documentação do J2SDK 1.5.0-beta1
apresenta as novidades de forma mais clara, mas ainda insuficiente.
Aqui tentarei “destrinchar” a nova plataforma tomando como base a
especificação formal, mas com uma abordagem mais coesa, mostrando
vantagens e usos práticos de cada item, para motivar o leitor.
Diversos
itens da especificação já foram explorados em artigos anteriores –
novidades da linguagem na Edição 12, e nova API de concorrência na
Edição 11 – portanto, vou apenas focar no que ainda não foi visto.
Também agrupei itens relacionados e reordenei alguns tópicos de acordo
com suas interdependências para estruturar melhor a explicação.
Confiabilidade e gerenciamento
Alguns
itens nesta seção podem parecer grego para muitos desenvolvedores.
Certas APIs, como a JPDA, são usadas apenas por quem faz ferramentas de
baixo nível, como depuradores e profilers. A maioria dos
desenvolvedores jamais precisará lidar com essas coisas – mas veremos
os resultados nas ferramentas. Mesmo a API java.util.concurrent é mais útil para quem implementa infra-estrutura.
Os
tipos genéricos estão neste grupo porque a robustez adicional é o
principal argumento a favor dessa melhoria, que elimina a possibilidade
da maioria das ClassCastException.
Veremos também que diversas interfaces da JVM precisaram ser
atualizadas para contemplar mudanças na linguagem, até em casos de
alterações aparentemente superficiais (“açúcar sintático”), como enums.
A maior novidade neste grupo: o Tiger inaugura uma nova era em qualidade de serviço
para Java. Teremos incluídos na JVM um grande número de recursos
avançados – de monitoração, gerenciamento, profiling, depuração etc. –
possibilitando analisar aplicações Java de forma tão fácil e
não-intrusiva (sem impacto em desempenho, segurança ou deployment),
que é bem possível que qualquer servidor em produção possa manter esses
recursos sempre ativados, agilizando a resolução de
bugs e de problemas de desempenho. A seguir, detalhes sobre cada feature
neste grupo.
Suporte a JMX (JSR-003)
A API javax.management já faz parte do J2EE, e agora será incluída também no J2SE. A mudança é necessária para o suporte a outras features do Tiger.
JMX (JSR-160)
Inclusão do conector JMX/RMI: javax.management.remote, javax.
management.remote.rmi. Estes conectores são um subconjunto da JMX Remote API, que também inclui pacotes opcionais não incluídos no Tiger.
API de monitoração e gerenciamento da JVM (JSR-174)
Atualmente,
o gerenciamento remoto da JVM é limitado e não padronizado.
Administradores de aplicações Java dependem de ferramentas
proprietárias, e que geralmente exigem algum container. No JRE 1.5, a
VM exporta um grande número de dados de monitoração e de desempenho
(cobrindo a atividade de garbage collection, compilador JIT, threads
etc.). Esses dados poderão ser acessados pelo código, como MBeans do
pacote java.lang.management, ou remotamente, via RMI ou SNMP.
Com os conectores remotos, consoles de administração existentes poderão ser usados para gerenciar uma JVM. A Figura 1 mostra
o console XtremeJ 2.0, que funciona como plug-in para o Eclipse,
conectado a um processo Java. Observe o enorme nível de detalhe das
informações exibidas. Nem dá para comparar com o parco volume de
informações que poderíamos obter até o J2SE 1.4 (por exemplo, usando Runtime.freeMemory()).
A ferramenta não depende de novas APIs do J2SE 1.5, mas com essa versão
irá detectar os MBeans da VM, além de MBeans de aplicação (como os
exportados pelo Tomcat, que usei no exemplo mostrado na figura).
Por
default, a configuração do recurso de monitoração só ativa os MBeans
que não têm impacto sobre o desempenho. Note que muitos destes
medidores já existiam nas JVMs anteriores, pois são necessários para a
inteligência do sofisticado runtime de Java – só não eram expostos por
uma API. Também, por default, a JVM não ativa nenhum conector remoto.
Se tais conectores forem ativados, seu acesso será seguro, exigindo
autenticação e encriptação via SSL. No diretório jre/lib/management da distribuição do Tiger, você encontrará novos arquivos de configuração que permitem customizar todas essas opções.
Em
vez de usar um console, o código da aplicação – ou o container, ou
outro componente do mesmo processo – pode invocar métodos da classe ManagementFactory
para obter os MBeans que descrevem o comportamento da JVM. Por
exemplo, você precisa saber quantas vezes algum thread ficou bloqueado
fazendo wait()? Fácil:
import java.lang.management.*;
ThreadMBean tbean =
ManagementFactory.getThreadMBean();
ThreadInfo tinfo =
tbean.getThreadInfo(Thread.currentThread().getId());
...
System.out.println(“Waits = “ + tinfo.getWaitedCount());
Essa
API é muito útil para técnicas avançadas, mas desenvolvedores e
administradores acharão mais fácil usar aplicações de monitoração,
como a mostrada anteriormente, que podem gerar gráficos, ou disparar
notificações quando certos valores excederem limites especificados etc.
Segurança de tipos em tempo de compilação com tipos genéricos (JSR-014)
Determina a inclusão de tipos genéricos. Veja a Edição 12 e também o quadro “A generalização do Java”.
API para gerar stack traces Java para todos os threads
Especifica os métodos Thread.getStackTrace()
e Thread.getAllStackTraces(). Atualmente, se você tem um processo Java que parece estar “em pane”, pode obter um dump de todos os threads, usando Ctrl+Break (Windows) ou Ctrl+\ (Unix).
Isso exige ter um console aberto para o processo, o que raramente
ocorre com aplicações em produção. No Unix, podemos usar kill –SIGQUIT pid para
obter o mesmo efeito sem um console, mas isso ainda exige intervenção
manual – e a sorte de acionar o dump num momento crítico. Com a nova
API, a aplicação poderá gerar esse dump de forma automática, por
exemplo ao capturar exceções críticas.
Por falar nisso, a interface Thread.UncaughtExceptionHandler permite definir tratadores de exceções não-capturadas. Se uma exceção não-checada (por exemplo, NullPointerException) não for tratada por nenhum catch
da aplicação, a JVM irá disparar o handler definido para este thread.
Se não houver um handler específico, será executado um handler global,
também configurável. Não havendo qualquer handler, a JVM recorre ao
procedimento padrão de versões anteriores do J2SE, que é abortar o
thread.
JPPA: profiling e instrumentação (JSR-163)
A Java Platform Profiling Architecture é uma API padronizada para profiling (análise de desempenho) da JVM. Substitui a velha JVMPI (Java Virtual Machine Profiling Interface),
que tinha status experimental e várias limitações. A nova API permitirá
a implementação de profilers mais eficientes e padronizados: qualquer
profiler será compatível com qualquer JVM, sem necessidade de usar JVMs
modificadas; novos profilers serão mais poderosos e eficientes.
Uma
das maiores vantagens da JPPA é permitir a ativação dinâmica de
profiling. Se uma aplicação em produção começar a apresentar problemas
de desempenho, um desenvolvedor poderá conectar-se ao processo
existente e iniciar o profiling – que pode ser seletivo, restrito às
classes de determinada aplicação de um container, por exemplo.
Terminada a análise, basta desativar o profiling para que o desempenho
do processo volte ao normal. Tudo sem reiniciar a aplicação e sem ter
que reproduzir o problema em ambiente de desenvolvimento.
A JPPA inclui o pacote java.lang.instrument, que permite criar, em 100% puro-Java, um instrumentador de bytecode,
código que intercepta o carregamento de classes e executa qualquer
modificação nessas classes dinamicamente. Um profiler pode usar isso,
por exemplo, para injetar, após todos os new, código que atualiza
estatísticas
de alocação. Mas é possível usar a API para qualquer técnica que
envolva a transformação do código. Um exemplo é a nova versão do IDE
IntelliJ, que usa instrumentação para gerar dinamicamente parte do
código de interface gráfica.
JPDA: adicionar suporte a Generics e enums
Adiciona suporte a tipos genéricos e enums às APIs, protocolos e ferramentas de depuração da JVM (JVMDI, JDWP, JDI, JDB).
JPDA: conexões e transportes plugáveis
Adiciona
à JPDA a capacidade de criar conectores customizados. Atualmente essa
API suporta sockets e memória compartilhada, mas será possível
adicionar outros transportes. Por exemplo, com um conector SOAP você
poderia depurar um processo executando na rede interna do cliente, sem
complicações, como instalar uma VPN ou abrir portas do firewall.
JDI: subconjunto read-only da JDI; métodos can...(); exceções
Melhorias para a JDI – Java Debugging Interface.
O item mais interessante é a operação read-only, que permite
depuradores conectar à JVM, mas só executar operações de leitura. Isso
torna possível deixar processos “abertos” para conexões de depuração,
sem perda de segurança.
JDI: clarificações sobre HotSwap
Corrige
alguns pontos obscuros da JDI em relação ao recurso de HotSwap
(“edite-e-continue”). A especificação atual permite incompatibilidades
entre alguns depuradores e JVMs.
JVMTI: requisitos para depuração
A
especificação diz que essa melhoria permitirá aos depuradores
acompanhar corretamente a inicialização e o encerramento da JVM. Isso
é bastante útil, mas a letra miúda dessa feature inclui um item pouco
relacionado que é meu favorito: “adicionar valor de retorno ao evento
de saída de método”. Esse recurso permite aos depuradores exibir o
valor retornado pelo último método invocado, o que atualmente não é
possível. Quando o retorno do método não é usado (ou é usado
diretamente como argumento de outra invocação ou expressão), muitos
programadores recorrem a uma variável dummy somente para saber o valor retornado. Isso deixará de ser necessário.
Adicionar bibliotecas de concorrência ao núcleo de Java (JSR-166)
...