Esse artigo faz parte da revista Java Magazine edição 54. Clique aqui para ler todos os artigos desta edição 

owtext; FONT-FAMILY: Verdana">Há poucos assuntos no universo Java tão controversos quanto sua história como plataforma para desktop – ou modernamente, “clientes ricos”. Nascido na forma de Applets para browsers, o Java rapidamente se deslocou para os datacenters com tecnologias como Servlets e EJB, e até mesmo para dispositivos como celulares, praticamente tomando conta do mercado em vários segmentos. Menos o desktop: neste caso, aparentemente o Java só definhou, pelo menos para os mais críticos.

A tecnologia do Java para desktop nunca ficou parada. Nesta coluna já examinamos melhorias na Swing, no desempenho da JVM, na funcionalidade do Java Web Start e outros fatores. Mas estas melhorias têm sido insuficientes. O Java obteve um sucesso apenas modesto para aplicações desktop em geral – embora seja bastante usado em alguns segmentos, como projetos que realmente precisam de GUIs ricas e portáveis entre várias plataformas, ou front-ends para aplicações corporativas. Mais grave é a situação do Java como plataforma para GUIs distribuídas via web: as applets foram varridas do mapa e não foram substituídas em massa por aplicações Swing, mas sim, por outras opções como Flash ou mesmo a tecnologia HTML renovada pelo AJAX.

A partir da JavaOne 2007, a Sun anunciou um contra-ataque: vários aperfeiçoamentos no JRE, que viriam resolver de um só golpe uma boa lista de reclamações históricas dos desenvolvedores que usam Java, especialmente com a Swing, para criar aplicações desktop. Esta promessa se tornou realidade, já estando disponíveis builds preliminares do “Java SE 6 Update N”. O release final estará pronto em agosto de 2008, portanto, este é um bom momento para começarmos a examinar esta atualização, que pode ser uma nova injeção de ânimo para o Java em um front que tem sido seu ponto fraco.

Problemas do Java para desktop

Começaremos fazendo uma revisão das dificuldades do Java no desktop, não só reclamando de tudo que está errado, mas procurando entender por que está errado. Esta seção nos ajudará a julgar se as melhorias do Update N resolvem o problema e também nos dará uma visão mais aprofundada dos problemas, não só do Java, mas também de outras plataformas modernas. (Em especial, a plataforma Microsoft .NET, que enfrenta muitos dos mesmos problemas discutidos aqui.)

Tempo de Carregamento

Um dos problemas mais perceptíveis de aplicações Java complexas é o tempo de inicialização. O administrador de um servidor de e-commerce não se incomoda em esperar um minuto ou dois para o boot de um cluster de servidor de aplicações paquidérmico. Mas para o usuário final de uma aplicação desktop, é irritante esperar até mesmo cinco segundos para carregar qualquer aplicação.

O tempo de carga é resultado da arquitetura de Virtual Machine do Java. Em uma aplicação nativa, os arquivos binários (como .exe e .dll no Windows) são estruturados de uma forma extremamente eficiente para determinada plataforma. Tanto o código quanto os dados são específicos para certa arquitetura de hardware & SO, e praticamente só precisam ser lidos do disco para a RAM[1]. Já os arquivos .class portáveis do Java precisam ser lidos, convertidos, e organizados (o layout em memória pode ser muito diferente do usado nestes arquivos). E depois, cada método será ou lentamente interpretado, ou compilado para código nativo – uma operação custosa, apesar de toda a avançada tecnologia de compiladores JIT dinâmicos como o HotSpot.

Alguns desenvolvedores se surpreendem ao ver que linguagens com runtimes menos sofisticados, como Perl, Python ou Ruby, não têm nenhum problema com tempo de inicialização. Mas isso ocorre devido à falta de desempenho destas linguagens, tipicamente interpretadas. É uma troca: o seu código Ruby pode começar a rodar imediatamente, mas tente escrever qualquer algoritmo complexo e você verá que o Java roda dezenas ou até centenas de vezes mais rápido. Se o tempo de inicialização é muito mais importante para você, experimente usar java –Xint, que executa a JVM em modo puramente interpretado. Para utilitários de execução breve, isso elimina o custo da compilação JIT e pode até melhorar o desempenho total.

Tamanho do JRE

O download do JRE pode ser um problema para quem distribui aplicações via web. O JRE 6.0 Update 3, por exemplo, tem quase 14Mb na versão internacionalizada para Windows. É quase 10 vezes maior que os 1,5Mb do Flash 9. Sem falar em HTML/AJAX, que não exige download algum.

Podemos dizer em defesa do Java que seu runtime não é grande demais: pelo contrário, é pequeno, para tudo que ele faz. Não há prova maior disso que compará-lo ao redistribuível do .NET 2.0, que mesmo evitando os custos da portabilidade, com seus 22Mb é quase 60% maior que o JRE (parte da diferença é justificada por algumas capacidades server-side, como ASP.NET).

Ocorre que tanto o Java SE quanto o .NET são plataformas muito mais ambiciosas que um Flash. O Java SE 6 inclui um toolkit de GUI completo (Swing) e uma API gráfica sofisticada (Java2D); muitas tecnologias de integração (sockets, RMI, CORBA, XML, web services, JDBC); facilidades de RAS como a JMX e a JVMTI; APIs de segurança digital, programação concorrente, e por aí vai. Em comparação, o Flash é uma linguagem de scripting com um runtime de baixo desempenho, um toolkit de GUI básico, e excelentes capacidades quase só para manipulação de mídia. Há APIs elementares para rede, XML e outras facilidades essenciais. Por exemplo, o pacote flash.xml possui somente três classes, com um parser/builder único (XMLDocument) algo equivalente a uma biblioteca DOM bem simples. Compare isso à funcionalidade (e ao desempenho) das APIs javax.xml do Java SE: SAX, DOM, StAX, XSLT, JAXB, SOAP, criptografia XML, validação...

A questão natural é muito simples: precisamos de tudo isso, para qualquer aplicação cliente? O sucesso de tecnologias pequenas e leves com o Flash indica que não. Existe, assim, a possibilidade de definir um “Java light” que possua apenas os recursos e APIs mais fundamentais, deixando o resto para pacotes opcionais que poderiam ser baixados em demanda, e exigidos apenas pelas aplicações que realmente utilizem estes pacotes.

Desempenho de execução

O desempenho da execução de código Java era um problema nos primeiros tempos, mas foi sendo progressivamente resolvido por JVMs cada vez mais avançadas. Atualmente, as melhores JVMs permitem um desempenho similar ao de linguagens nativas de baixo nível, como C/C++. Mas isso tem alguns custos, como o tempo de carga piorado pelo processo de compilação JIT.

Além disso, a dependência de compiladores JIT avançados pode ser um problema. As otimizações que consomem mais recursos existem apenas nas JVMs otimizadas para servidores, como o HotSpot Server (java –server), que têm um tempo de carga e consumo de memória maior, e não estão disponíveis no JRE (só no JDK), dificultando a distribuição via internet.

Com outras linguagens competidoras, apesar de menos sofisticadas, parecem não ter problemas de desempenho? Por exemplo, dissemos que o Flash tem um runtime de baixo desempenho, no entanto as aplicações Flash não parecem lentas. O mesmo pode ser dito de outros casos, como o Ruby.

Um dos segredos é que estas linguagens não dão a mesma ênfase que o Java para a portabilidade. Todas as bibliotecas “difíceis”, como os codecs de mídia do Flash ou as expressões regulares do Perl, são implementadas pelo runtime nativo, isto é, em C. Para as bibliotecas-padrão da linguagem isso não é um grande problema, basta que este código C seja portado para todas as plataformas (e não é muito: poucas concorrentes têm uma fração das APIs do Java SE). Geralmente, o Java usa código nativo apenas em APIs que exigem forte interação com o SO (I/O, AWT, etc.); se fosse usar para todas que têm requisitos de alto desempenho, seria um volume demasiado de código nativo, dificultando o porte entre plataformas, que é uma prioridade da implementação do Java.

O grande problema é a necessidade de desempenho em código da própria aplicação, ou mesmo de frameworks externos ao núcleo da linguagem (ex.: o Rails do Ruby). Se estes possuírem algoritmos que necessitem de uma linguagem eficiente, precisam ser implementados com código nativo, o que tem grande impacto na portabilidade real de linguagens ditas multiplataforma, e na produtividade real de linguagens vendidas pelos seus fãs como ultra-fáceis de programar. De que isso adianta, se por motivo de desempenho, você acaba tendo que escrever 20% da aplicação numa linguagem de péssima produtividade como C?

Este problema só não afeta a maioria das aplicações destas linguagens, pois quase todas as suas aplicações se confinam a nichos estreitos: por exemplo, ActionScript (Flash) só é usado para GUIs e multimídia, portanto o grosso do processamento é feito por componentes nativos como controles e codecs. O Ruby on Rails é usado quase só para front-ends web para bancos de dados, então o grosso do trabalho é feito por aplicações nativas como o webserver e o servidor de BD, e assim por diante.

Consumo de memória

O consumo e memória é outra mancha na imagem do Java. É difícil não nos horrorizarmos com o demo SwingSet2 do JDK, que usa aproximadamente 70Mb. Por que as aplicações Java usam tanta memória a mais que aplicações nativas (e mesmo, outras linguagens com VMs como Python, etc.)?

Parte da resposta é o preço do runtime sofisticado do Java. Um compilador JIT avançado como o HotSpot ocupa alguns Mb a mais que um simples interpretador (sem falar numa aplicação nativa, com 0 bytes de overhead para isso – pois já foi compilada de antemão). ...

Quer ler esse conteúdo completo? Tenha acesso completo