Garbage Collection em Java (1)
Este é o primeiro de uma série de artigos sobre Garbage Collection em Java, traduzidos dos originais publicados por Richard Warburton no seu blog. (Tradução e publicação autorizadas pelo autor.)
Visão geral da Heap
Espero ser capaz de cobrir um pouco da teoria e analisar todos os coletores mais importantes na máquina virtual hotspot,1 ao longo desta série. Este artigo explica apenas o que é garbage collection e os elementos comuns aos diferentes coletores.
Por que devo me preocupar?
Sua máquina virtual Java gerencia a memória para você – o que é muito conveniente –, mas ela pode não vir regulada por padrão no ponto de seu melhor desempenho. Ao compreender um pouco da teoria por trás da garbage collection você poderá ajustar o seu coletor mais facilmente. Uma preocupação comum é com a eficiência do coletor, ou seja, quanto tempo o programa gasta executando o código, em relação ao tempo dispendido na coleta de lixo. Outra preocupação usual é quanto ao tempo em que o aplicativo fica parado.
Além disso, existem muitas lendas e boatos envolvendo garbage collection, de forma que a compreensão dos algorítimos um pouco mais detalhadamente ajuda a não cair nas ciladas e armadilhas costumeiras. Além disso, para qualquer um que se interesse em aprender como são aplicados e utilizados na prática os princípios da ciência da computação, é muito compensador examinar as entranhas da JVM.
O que significa “parar o mundo”?
Seu programa (ou mutator usando a terminologia GC) está sempre criando objetos à medida em que é executado. Em algum ponto a heap precisa ser coletada e todos os coletores na hotspot param o aplicativo. O termo “parar o mundo” significa que todas as threads do mutator são pausadas.
É possível implementar um garbage collector que não precise efetuar pausas. A Azul Systems implementou efetivamente um coletor que não precisa de pausas na sua máquina virtual Zing. Não vou explicar aqui como ele funciona, mas há um whitepaper muito interessante, caso você queira se aprofundar no assunto.
A Hipótese Geracional Jovem Fraca
Trocando em miúdos: A maioria dos objetos alocados morrem jovens2. Este conceito foi demonstrado empiricamente analisando a alocação de memória e os padrões de sobrevida de um grande grupo de programas durante a década de 1980. O que os pesquisadores descobriram foi não apenas que a maioria dos objetos morrem jovens, mas também que, uma vez ultrapassada certa idade, tendem a ter uma sobrevida longa. O gráfico abaixo foi tirado de um estudo da SUN/Oracle observando a vida útil dos objetos na forma de um histograma.
Como é organizada a Heap?
A hipótese geracional jovem deu origem à ideia de garbage collection geracional na qual a heap é dividida em várias regiões, e a colocação dos objetos dentro de cada região corresponde à sua idade. Um elemento que é comum a todos os garbage collectors mencionados acima (todos menos o G1)3 é a maneira como a heap é organizada em diferentes espaços.
Quando os objetos são inicialmente alocados, se couberem eles são armazenados no espaço doEden. Caso o objeto sobreviva a uma coleta passa então para o espaço Survivor. Se ele sobreviver algumas vezes (o limiar de tenuring), passa então para o espaço Tenured (vitalício). As especificidades dos algorítimos para coletar esses espaços diferem de acordo com o coletor e, assim sendo, vou analisá-los separadamente num artigo futuro.
Esta divisão é benéfica porque permite usar diferentes algorítimos em espaços diferentes. Alguns algorítimos GC são mais eficientes se a maioria dos seus objetos estiver morta e outros são mais eficientes se a maioria deles estiver viva. Em razão da hipótese geracional, normalmente, quando chega a hora de coletar, a maioria dos objetos nos espaços Eden e Survivor estão mortos, e a maioria dos objetos em Tenured estão vivos.
Há ainda a Permgen – ou geração permanente. Esta é uma geração especial que mantém os objetos relacionados à própria linguagem Java. As informações sobre as classes carregadas, por exemplo, são mantidas ali. Historicamente, Strings que tenham sido internadas ou sejam constantes, também são lá mantidas. A geração permanente está sendo trocada pelo metaspace.
Múltiplos Coletores
A máquina virtual hotspot tem, na verdade, uma diversidade de Garbage Collectors. Cada um possui um conjunto diferente de características de desempenho e é mais (ou menos) adequado para diferentes tarefas. Os Garbage Collectors chave que vou examinar são:
- Parallel Scavenge (PS) [Limpador Paralelo]: o coletor default nas edições recentes das JVM. Eles param o mundo para poder coletar, mas o fazem em paralelo (i.e. usando threads múltiplos).
- Concurrent Mark Sweep (CMS) [Marcador de Varrição Concorrente]: este coletor tem diversas fases, algumas das quais param o mundo, mas também rodam concorrentemente com o programa em várias de suas fases.
- Incremental Concurrent Mark Sweep (iCMS) [ Marcador de Varrição Concorrente Incremental]: uma variante do CMS, projetado para pausas menores. Algumas vezes ele consegue fazer isso!
- Garbage First (G1) [Primeiro Lixeiro]: um novo coletor que recentemente se tornou mais estável e cujo uso vem aumentando aos poucos.
Conclusões
Apresentei alguns tópicos para reflexão introdutória sobre garbage collection. No próximo artigo vou analisar o coletor Parallel Scavenge – que é atualmente o coletor padrão. Gostaria também de fornecer um link para o meu empregador, que tem um GC log analyser (analisador do log dos GC), o qual acreditamos possa ser muito útil.
Notas:
1 hotspot é o nome dado à base de código comum por trás do openjdk e da JVM oficial da Oracle. A partir de Java 7, o openjdk é a implementação de referência para a Java SE.
2 Tecnicamente o que eu descrevi acima é a “hipótese geracional fraca” cuja validação é empírica. Há também uma variante forte, que pode ser assim declarada: “o tempo médio de vida de um objeto criado na heap é igual à quantidade média do meio de armazenamento disponível”. Isso na verdade pode ser provado matematicamente usando a Lei de Little e considerando λ igual a 1. Prova muito simples!
3 Explicarei a forma como a heap é organizada dentro do G1 num artigo específico sobre o tema.