Observação : estou trabalhando na versão 2 deste guia e preciso da sua ajuda! Use este formulário para me dar feedback sobre o que você acha que deveria ser incluído na próxima versão. Obrigado!
Java é uma das linguagens de programação mais populares que existe, mas ninguém parece gostar de usá-la. Bem, Java é na verdade uma boa linguagem de programação e, como o Java 8 foi lançado recentemente, decidi compilar uma lista de bibliotecas, práticas e ferramentas para melhorar o uso do Java. "Melhor" é subjetivo, então eu recomendaria pegar as partes que falam com você e usá-las, em vez de tentar usar todas elas de uma vez. Sinta-se à vontade para enviar solicitações pull sugerindo acréscimos.
Este artigo foi postado originalmente em meu blog.
Leia isto em outros idiomas: Inglês, 简体中文
Tradicionalmente, Java era programado em um estilo JavaBean corporativo muito detalhado. O novo estilo é muito mais limpo, correto e agradável aos olhos.
Uma das coisas mais simples que nós, como programadores, fazemos é transmitir dados. A maneira tradicional de fazer isso é definir um JavaBean:
public class DataHolder {
private String data ;
public DataHolder () {
}
public void setData ( String data ) {
this . data = data ;
}
public String getData () {
return this . data ;
}
}
Isso é prolixo e um desperdício. Mesmo que o seu IDE tenha gerado esse código automaticamente, é um desperdício. Então, não faça isso.
Em vez disso, prefiro o estilo C struct de escrever classes que apenas armazenam dados:
public class DataHolder {
public final String data ;
public DataHolder ( String data ) {
this . data = data ;
}
}
Esta é uma redução no número de linhas de código pela metade. Além disso, esta classe é imutável, a menos que você a estenda, então podemos raciocinar sobre ela com mais facilidade, pois sabemos que ela não pode ser alterada.
Se você estiver armazenando objetos como Map ou List que podem ser modificados facilmente, você deve usar ImmutableMap ou ImmutableList, que é discutido na seção sobre imutabilidade.
Se você tiver um objeto bastante complicado para o qual deseja construir uma estrutura, considere o padrão Builder.
Você cria uma classe interna estática que construirá seu objeto. Ele usa um estado mutável, mas assim que você chamar build, ele emitirá um objeto imutável.
Imagine que tivéssemos um DataHolder mais complicado. O construtor para isso pode ser parecido com:
public class ComplicatedDataHolder {
public final String data ;
public final int num ;
// lots more fields and a constructor
public static class Builder {
private String data ;
private int num ;
public Builder data ( String data ) {
this . data = data ;
return this ;
}
public Builder num ( int num ) {
this . num = num ;
return this ;
}
public ComplicatedDataHolder build () {
return new ComplicatedDataHolder ( data , num ); // etc
}
}
}
Então para usá-lo:
final ComplicatedDataHolder cdh = new ComplicatedDataHolder . Builder ()
. data ( "set this" )
. num ( 523 )
. build ();
Existem exemplos melhores de Construtores em outros lugares, mas isso deve lhe dar uma ideia de como é. Isso acaba com muitos dos clichês que estávamos tentando evitar, mas oferece objetos imutáveis e uma interface muito fluente.
Em vez de criar objetos construtores manualmente, considere usar uma das muitas bibliotecas que podem ajudá-lo a gerar construtores.
Se você criar muitos objetos imutáveis manualmente, considere usar o processador de anotações para gerá-los automaticamente a partir de interfaces. Isso minimiza o código padrão, reduz a probabilidade de bugs e promove a imutabilidade. Veja esta apresentação para uma discussão interessante sobre alguns dos problemas com os padrões normais de codificação Java.
Algumas ótimas bibliotecas de geração de código são imutáveis, valor automático do Google e Lombok.
As exceções verificadas devem ser usadas com cautela, se for o caso. Eles forçam seus usuários a adicionar muitos blocos try/catch e agrupar suas próprias exceções. Melhor é fazer com que suas exceções estendam RuntimeException. Isso permite que seus usuários tratem suas exceções da maneira que gostariam, em vez de forçá-los a tratar/declarar que isso é lançado sempre, o que polui o código.
Um truque bacana é colocar RuntimeExceptions na declaração throws do seu método. Isso não tem efeito no compilador, mas informará aos usuários por meio da documentação que essas exceções podem ser lançadas.
Esta é mais uma seção de engenharia de software do que uma seção Java, mas uma das melhores maneiras de escrever software testável é usar injeção de dependência (DI). Como Java incentiva fortemente o design OO, para criar software testável, você precisa usar DI.
Em Java, isso normalmente é feito com o Spring Framework. Ele tem uma ligação baseada em código ou uma ligação baseada em configuração XML. Se você usar a configuração XML, é importante não usar excessivamente o Spring devido ao seu formato de configuração baseado em XML. Não deve haver absolutamente nenhuma lógica ou estrutura de controle em XML. Deve apenas injetar dependências.
Boas alternativas para usar o Spring são a biblioteca Dagger do Google e Square ou o Guice do Google. Eles não usam o formato de arquivo de configuração XML do Spring e, em vez disso, colocam a lógica de injeção em anotações e no código.
Tente evitar o uso de nulos quando puder. Não retorne coleções nulas quando deveria ter retornado uma coleção vazia. Se você for usar null, considere a anotação @Nullable. O IntelliJ IDEA possui suporte integrado para a anotação @Nullable.
Leia mais sobre por que não usar nulos em O pior erro da ciência da computação.
Se você estiver usando Java 8, poderá usar o novo e excelente tipo Opcional. Se um valor pode ou não estar presente, envolva-o em uma classe Opcional como esta:
public class FooWidget {
private final String data ;
private final Optional < Bar > bar ;
public FooWidget ( String data ) {
this ( data , Optional . empty ());
}
public FooWidget ( String data , Optional < Bar > bar ) {
this . data = data ;
this . bar = bar ;
}
public Optional < Bar > getBar () {
return bar ;
}
}
Então agora está claro que os dados nunca serão nulos, mas bar pode ou não estar presente. Opcional possui métodos como isPresent , o que pode fazer parecer que não há muita diferença em apenas verificar null . Mas permite que você escreva declarações como:
final Optional < FooWidget > fooWidget = maybeGetFooWidget ();
final Baz baz = fooWidget . flatMap ( FooWidget :: getBar )
. flatMap ( BarWidget :: getBaz )
. orElse ( defaultBaz );
O que é muito melhor do que verificações nulas encadeadas. A única desvantagem de usar Opcional é que a biblioteca padrão não tem um bom suporte Opcional, portanto, ainda é necessário lidar com nulos.
A menos que você tenha um bom motivo para fazê-las de outra forma, variáveis, classes e coleções devem ser imutáveis.
Variáveis podem se tornar imutáveis com final :
final FooWidget fooWidget ;
if ( condition ()) {
fooWidget = getWidget ();
} else {
try {
fooWidget = cachedFooWidget . get ();
} catch ( CachingException e ) {
log . error ( "Couldn't get cached value" , e );
throw e ;
}
}
// fooWidget is guaranteed to be set here
Agora você pode ter certeza de que fooWidget não será reatribuído acidentalmente. A palavra-chave final funciona com blocos if/else e com blocos try/catch. Claro, se o fooWidget em si não for imutável, você poderá facilmente alterá-lo.
As coleções devem, sempre que possível, usar as classes Guava ImmutableMap, ImmutableList ou ImmutableSet. Eles possuem construtores para que você possa construí-los dinamicamente e marcá-los como imutáveis chamando o método build.
As classes devem se tornar imutáveis declarando campos imutáveis (via final ) e usando coleções imutáveis. Opcionalmente, você pode tornar a classe final para que ela não possa ser estendida e tornada mutável.
Tenha cuidado se você estiver adicionando muitos métodos a uma classe Util.
public class MiscUtil {
public static String frobnicateString ( String base , int times ) {
// ... etc
}
public static void throwIfCondition ( boolean condition , String msg ) {
// ... etc
}
}
Essas classes, a princípio, parecem atraentes porque os métodos nelas contidos não pertencem realmente a nenhum lugar. Então você coloca todos eles aqui em nome da reutilização de código.
A cura é pior que a doença. Coloque essas classes onde elas pertencem e refatore agressivamente. Não nomeie classes, pacotes ou bibliotecas com nomes muito genéricos, como "MiscUtils" ou "ExtrasLibrary". Isso incentiva o despejo de código não relacionado lá.
A formatação é muito menos importante do que a maioria dos programadores imagina. A consistência mostra que você se preocupa com seu trabalho e ajuda os outros a ler? Absolutamente. Mas não vamos perder um dia adicionando espaços aos blocos if para que "correspondam".
Se você realmente precisa de um guia de formatação de código, recomendo fortemente o guia de estilo Java do Google. A melhor parte desse guia é a seção Práticas de Programação. Definitivamente vale a pena ler.
Documentar o código voltado para o usuário é importante. E isso significa usar exemplos e descrições sensatas de variáveis, métodos e classes.
O corolário disso é não documentar o que não precisa ser documentado. Se você não tem nada a dizer sobre o que é um argumento, ou se ele é óbvio, não o documente. A documentação padrão é pior do que nenhuma documentação, pois engana os usuários fazendo-os pensar que existe documentação.
Java 8 tem um bom fluxo e sintaxe lambda. Você poderia escrever um código como este:
final List < String > filtered = list . stream ()
. filter ( s -> s . startsWith ( "s" ))
. map ( s -> s . toUpperCase ())
. collect ( Collectors . toList ());
Em vez disso:
final List < String > filtered = new ArrayList <>();
for ( String str : list ) {
if ( str . startsWith ( "s" ) {
filtered . add ( str . toUpperCase ());
}
}
Isso permite que você escreva um código mais fluente e mais legível.
Implantar o Java corretamente pode ser um pouco complicado. Existem duas maneiras principais de implantar Java hoje em dia: usar uma estrutura ou usar uma solução caseira que seja mais flexível.
Como implantar Java não é fácil, foram criadas estruturas que podem ajudar. Dois dos melhores são Dropwizard e Spring Boot. A estrutura Play também pode ser considerada uma dessas estruturas de implantação.
Todos eles tentam diminuir a barreira para divulgar seu código. Eles são especialmente úteis se você for novo em Java ou se precisar fazer as coisas rapidamente. Implantações JAR únicas são mais fáceis do que implantações complicadas de WAR ou EAR.
No entanto, eles podem ser um tanto inflexíveis e bastante teimosos, portanto, se o seu projeto não se enquadrar nas escolhas feitas pelos desenvolvedores do seu framework, você terá que migrar para uma configuração mais manual.
Boa alternativa : Gradle.
Maven ainda é a ferramenta padrão para construir, empacotar e executar seus testes. Existem alternativas, como o Gradle, mas não têm a mesma adoção do Maven. Se você é novo no Maven, você deve começar com o Maven por Exemplo.
Gosto de ter um POM root com todas as dependências externas que você deseja usar. Será algo parecido com isto. Este POM raiz tem apenas uma dependência externa, mas se o seu produto for grande o suficiente, você terá dezenas. Seu POM raiz deve ser um projeto por si só: com controle de versão e lançado como qualquer outro projeto Java.
Se você acha que marcar seu POM raiz para cada mudança de dependência externa é demais, você não perdeu uma semana rastreando erros de dependência entre projetos.
Todos os seus projetos Maven incluirão seu POM raiz e todas as informações de sua versão. Dessa forma, você obtém a versão selecionada de cada dependência externa pela sua empresa e todos os plug-ins corretos do Maven. Se você precisar extrair dependências externas, funciona assim:
< dependencies >
< dependency >
< groupId >org.third.party</ groupId >
< artifactId >some-artifact</ artifactId >
</ dependency >
</ dependencies >
Se você quiser dependências internas, elas devem ser gerenciadas por cada projeto individual seção. Caso contrário, seria difícil manter o número da versão raiz do POM correto.
Uma das melhores partes do Java é a enorme quantidade de bibliotecas de terceiros que fazem tudo. Essencialmente, toda API ou kit de ferramentas possui um SDK Java e é fácil de aplicá-lo com o Maven.
E essas próprias bibliotecas Java dependem de versões específicas de outras bibliotecas. Se você extrair bibliotecas suficientes, terá conflitos de versão, ou seja, algo assim:
Foo library depends on Bar library v1.0
Widget library depends on Bar library v0.9
Qual versão será incluída no seu projeto?
Com o plug-in de convergência de dependências do Maven, a compilação apresentará erros se suas dependências não usarem a mesma versão. Então, você tem duas opções para resolver o conflito:
A escolha de qual escolher depende da sua situação: se você deseja acompanhar a versão de um projeto, então excluir faz sentido. Por outro lado, se quiser ser explícito, você pode escolher uma versão, embora seja necessário atualizá-la ao atualizar as outras dependências.
Obviamente, você precisa de algum tipo de servidor de integração contínua que irá construir continuamente suas versões SNAPSHOT e construções de tags com base em tags git.
Jenkins e Travis-CI são escolhas naturais.
A cobertura de código é útil e a Cobertura possui um bom plugin Maven e suporte CI. Existem outras ferramentas de cobertura de código para Java, mas eu usei a Cobertura.
Você precisa de um lugar para colocar seus JARs, WARs e EARs que você cria, então você precisará de um repositório.
As escolhas comuns são Artifactory e Nexus. Ambos funcionam e têm seus prós e contras.
Você deve ter sua própria instalação do Artifactory/Nexus e espelhar suas dependências nela. Isso impedirá que sua compilação seja interrompida porque algum repositório Maven upstream caiu.
Agora você compilou seu código, configurou seu repositório e precisa colocá-lo em seu ambiente de desenvolvimento e, eventualmente, enviá-lo para produção. Não economize aqui, porque automatizar isso renderá dividendos por muito tempo.
Chef, Puppet e Ansible são escolhas típicas. Escrevi uma alternativa chamada Esquadrão, que, claro, acho que você deveria verificar porque é mais fácil acertar do que as alternativas.
Independentemente da ferramenta que você escolher, não se esqueça de automatizar suas implantações.
Provavelmente a melhor característica do Java é a grande quantidade de bibliotecas que ele possui. Esta é uma pequena coleção de bibliotecas que provavelmente será aplicável ao maior grupo de pessoas.
A biblioteca padrão do Java, que já foi um avanço incrível, agora parece que faltam vários recursos importantes.
O projeto Apache Commons possui várias bibliotecas úteis.
Commons Codec tem muitos métodos úteis de codificação/decodificação para Base64 e strings hexadecimais. Não perca seu tempo reescrevendo isso.
Commons Lang é a biblioteca ideal para manipulação e criação de Strings, conjuntos de caracteres e vários métodos utilitários diversos.
Commons IO tem todos os métodos relacionados a arquivos que você poderia desejar. Possui FileUtils.copyDirectory, FileUtils.writeStringToFile, IOUtils.readLines e muito mais.
Guava é a excelente biblioteca do Google, aqui está o que está faltando em Java. É quase difícil destilar tudo o que gosto nesta biblioteca, mas vou tentar.
Cache é uma maneira simples de obter um cache na memória que pode ser usado para armazenar em cache o acesso à rede, o acesso ao disco, memorizar funções ou qualquer coisa realmente. Basta implementar um CacheBuilder que diga ao Guava como construir seu cache e está tudo pronto!
Coleções imutáveis . Há vários deles: ImmutableMap, ImmutableList ou mesmo ImmutableSortedMultiSet, se esse for o seu estilo.
Também gosto de escrever coleções mutáveis do jeito Guava:
// Instead of
final Map < String , Widget > map = new HashMap <>();
// You can use
final Map < String , Widget > map = Maps . newHashMap ();
Existem classes estáticas para Listas, Mapas, Conjuntos e muito mais. Eles são mais limpos e fáceis de ler.
Se você estiver preso ao Java 6 ou 7, poderá usar a classe Collections2, que possui métodos como filter e transform. Eles permitem que você escreva código fluente sem o suporte de fluxo do Java 8.
O Guava também tem coisas simples, como um Joiner que une strings em separadores e uma classe para lidar com interrupções, ignorando-as.
A biblioteca Gson do Google é uma biblioteca de análise JSON simples e rápida. Funciona assim:
final Gson gson = new Gson ();
final String json = gson . toJson ( fooWidget );
final FooWidget newFooWidget = gson . fromJson ( json , FooWidget . class );
É muito fácil e um prazer trabalhar com ele. O guia do usuário do Gson tem muitos mais exemplos.
Um dos meus aborrecimentos constantes com Java é que ele não possui tuplas incorporadas à biblioteca padrão. Felizmente, o projeto de tuplas Java corrige isso.
É simples de usar e funciona muito bem:
Pair < String , Integer > func ( String input ) {
// something...
return Pair . with ( stringResult , intResult );
}
Javaslang é uma biblioteca funcional, projetada para adicionar recursos ausentes que deveriam fazer parte do Java 8. Alguns desses recursos são
Existem várias bibliotecas Java que dependem das coleções Java originais. Eles são restritos para permanecerem compatíveis com classes que foram criadas com foco orientado a objetos e projetadas para serem mutáveis. As coleções Javaslang para Java são completamente novas, inspiradas em Haskell, Clojure e Scala. Eles são criados com foco funcional e seguem um design imutável.
Código como este é automaticamente thread-safe e try-catch free:
// Success/Failure containing the result/exception
public static Try < User > getUser ( int userId ) {
return Try . of (() -> DB . findUser ( userId ))
. recover ( x -> Match . of ( x )
. whenType ( RemoteException . class ). then ( e -> ...)
. whenType ( SQLException . class ). then ( e -> ...));
}
// Thread-safe, reusable collections
public static List < String > sayByeBye () {
return List . of ( "bye, " bye ", "collect" , "mania" )
. map ( String :: toUpperCase )
. intersperse ( " " );
}
Joda-Time é facilmente a melhor biblioteca de tempo que já usei. Simples, direto e fácil de testar. O que mais você pode pedir?
Você só precisa disso se ainda não estiver no Java 8, pois ele possui sua própria nova biblioteca de tempo que não é uma droga.
Lombok é uma biblioteca interessante. Por meio de anotações, ele permite reduzir o clichê que o Java tanto sofre.
Quer setters e getters para suas variáveis de classe? Simples:
public class Foo {
@ Getter @ Setter private int var ;
}
Agora você pode fazer isso:
final Foo foo = new Foo ();
foo . setVar ( 5 );
E há muito mais. Ainda não usei o Lombok em produção, mas mal posso esperar.
Boas alternativas : Jersey ou Spark
Existem dois campos principais para executar serviços web RESTful em Java: JAX-RS e tudo mais.
JAX-RS é a forma tradicional. Você combina anotações com interfaces e implementações para formar o serviço web usando algo como Jersey. O que é legal nisso é que você pode facilmente criar clientes apenas com a classe de interface.
A estrutura do Play é uma abordagem radicalmente diferente dos serviços da Web na JVM: você tem um arquivo de rotas e depois escreve as classes referenciadas nessas rotas. Na verdade, é uma estrutura MVC completa, mas você pode usá-la facilmente apenas para serviços da web REST.
Está disponível para Java e Scala. Ele sofre um pouco por ser o primeiro Scala, mas ainda é bom para usar em Java.
Se você está acostumado com microframeworks como Flask em Python, o Spark será muito familiar. Funciona especialmente bem com Java 8.
Existem muitas soluções de log Java por aí. Meu favorito é o SLF4J porque é extremamente conectável e pode combinar logs de muitas estruturas de log diferentes ao mesmo tempo. Tem um projeto estranho que usa java.util.logging, JCL e log4j? SLF4J é para você.
O manual de duas páginas é praticamente tudo que você precisa para começar.
Não gosto de estruturas ORM pesadas porque gosto de SQL. Então, escrevi muitos modelos JDBC e era meio difícil de manter. jOOQ é uma solução muito melhor.
Ele permite que você escreva SQL em Java de maneira segura:
// Typesafely execute the SQL statement directly with jOOQ
Result < Record3 < String , String , String >> result =
create . select ( BOOK . TITLE , AUTHOR . FIRST_NAME , AUTHOR . LAST_NAME )
. from ( BOOK )
. join ( AUTHOR )
. on ( BOOK . AUTHOR_ID . equal ( AUTHOR . ID ))
. where ( BOOK . PUBLISHED_IN . equal ( 1948 ))
. fetch ();
Usando isso e o padrão DAO, você pode facilitar o acesso ao banco de dados.
O teste é fundamental para o seu software. Esses pacotes ajudam a tornar tudo mais fácil.
Boa alternativa : TestNG.
jUnit dispensa apresentações. É a ferramenta padrão para testes unitários em Java.
Mas provavelmente você não está usando o jUnit em todo o seu potencial. jUnit oferece suporte a testes parametrizados, regras para impedir que você escreva tantos clichês, teorias para testar aleatoriamente determinados códigos e suposições.
Se você fez sua injeção de dependência, é aqui que vale a pena: zombar do código que tem efeitos colaterais (como falar com um servidor REST) e ainda afirmar o comportamento do código que o chama.
jMock é a ferramenta de simulação padrão para Java. Parece assim:
public class FooWidgetTest {
private Mockery context = new Mockery ();
@ Test
public void basicTest () {
final FooWidgetDependency dep = context . mock ( FooWidgetDependency . class );
context . checking ( new Expectations () {{
oneOf ( dep ). call ( with ( any ( String . class )));
atLeast ( 0 ). of ( dep ). optionalCall ();
}});
final FooWidget foo = new FooWidget ( dep );
Assert . assertTrue ( foo . doThing ());
context . assertIsSatisfied ();
}
}
Isso configura um FooWidgetDependency via jMock e depois adiciona expectativas. Esperamos que o método de chamada de dep seja chamado uma vez com alguma String e que o método opcionalCall de dep seja chamado zero ou mais vezes.
Se você precisar configurar a mesma dependência repetidamente, provavelmente deverá colocá-la em um acessório de teste e colocar assertIsSatisfied em um acessório @After .
Você já fez isso com jUnit?
final List < String > result = some . testMethod ();
assertEquals ( 4 , result . size ());
assertTrue ( result . contains ( "some result" ));
assertTrue ( result . contains ( "some other result" ));
assertFalse ( result . contains ( "shouldn't be here" ));
Este é apenas um clichê irritante. AssertJ resolve isso. Você pode transformar o mesmo código nisso:
assertThat ( some . testMethod ()). hasSize ( 4 )
. contains ( "some result" , "some other result" )
. doesNotContain ( "shouldn't be here" );
Essa interface fluente torna seus testes mais legíveis. O que mais você poderia querer?
Boas alternativas : Eclipse e Netbeans
O melhor IDE Java é o IntelliJ IDEA. Ele tem muitos recursos incríveis e é realmente a principal coisa que torna a verbosidade do Java suportável. O preenchimento automático é ótimo, as inspeções são excelentes e as ferramentas de refatoração são realmente úteis.
A edição comunitária gratuita é boa o suficiente para mim, mas há muitos recursos excelentes na edição Ultimate, como ferramentas de banco de dados, suporte ao Spring Framework e Chronon.
Um dos meus recursos favoritos do GDB 7 foi a capacidade de voltar no tempo durante a depuração. Isso é possível com o plugin Chronon IntelliJ quando você obtém a edição Ultimate.
Você obtém histórico de variáveis, retrocesso, histórico de métodos e muito mais. É um pouco estranho usar pela primeira vez, mas pode ajudar a depurar alguns bugs realmente complexos, Heisenbugs e similares.
Boa alternativa : DCEVM
A integração contínua costuma ser uma meta dos produtos de software como serviço. E se você nem precisasse esperar a conclusão da compilação para ver as alterações no código ao vivo?
É isso que JRebel faz. Depois de conectar seu servidor ao cliente JRebel, você poderá ver as alterações em seu servidor instantaneamente. É uma grande economia de tempo quando você deseja experimentar rapidamente.
O sistema de tipos do Java é bastante fraco. Ele não diferencia Strings e Strings que são na verdade expressões regulares, nem faz qualquer verificação de contaminação. No entanto, o Checker Framework faz isso e muito mais.
Ele usa anotações como @Nullable para verificar tipos. Você pode até definir suas próprias anotações para tornar a análise estática ainda mais poderosa.
Mesmo seguindo as melhores práticas, até o melhor desenvolvedor cometerá erros. Existem várias ferramentas que você pode usar para validar seu código Java e detectar problemas em seu código. Abaixo está uma pequena seleção de algumas das ferramentas mais populares. Muitos deles se integram a IDEs populares, como Eclipse ou IntelliJ, permitindo que você identifique erros em seu código mais rapidamente.
Além de usar essas ferramentas durante o desenvolvimento, geralmente é uma boa ideia executá-las também durante os estágios de construção. Eles podem ser vinculados a ferramentas de construção como Maven ou Gradle e também a ferramentas de integração contínua.
Vazamentos de memória acontecem, mesmo em Java. Felizmente, existem ferramentas para isso. A melhor ferramenta que usei para corrigir isso é o Eclipse Memory Analyzer. É necessário um despejo de pilha e permite encontrar o problema.
Existem algumas maneiras de obter um heap dump para um processo JVM, mas eu uso o jmap:
$ jmap -dump:live,format=b,file=heapdump.hprof -F 8152
Attaching to process ID 8152, please wait...
Debugger attached successfully.
Server compiler detected.
JVM version is 23.25-b01
Dumping heap to heapdump.hprof ...
... snip ...
Heap dump file created
Então você pode abrir o arquivo heapdump.hprof com o Memory Analyzer e ver o que está acontecendo rapidamente.
Recursos para ajudá-lo a se tornar um mestre em Java.