Back-End

3 fev, 2015

Calculando o tamanho do objeto Graph na JVM

Publicidade

Neste artigo, vou falar um pouco sobre algo que eu aprendi recentemente. Há pouco tempo, tive a tarefa de adicionar um cache de memória a um serviço web de taxa de câmbio em um projeto no qual estou trabalhando. Usei o CacheBuilder da Guava e defini alguns valores aleatórios para seu tamanho máximo e prazo de validade do cache. O que tenho me perguntado é qual seria a quantidade de memória que esse cache vai precisar quando estiver cheio?

Soube de uma ferramenta chamada Jol (Java Object Layout) e que poderia ser exatamente o que eu precisava, mas eu não tinha certeza. Investiguei mais a fundo e, com certeza, é uma ferramenta muito boa para encontrar não só o tamanho da memória de um objeto Graph, mas também a forma como os objetos são colocados para fora na memória em diferentes JVMs. Esse recurso é útil quando mais tarde for feita a otimização de caches de CPU, mas não tenho nenhuma experiência com isso ainda.

Voltando para o assunto deste artigo – encontrei a quantidade total de memória para um objeto Graph e pude notar que as coisas são realmente muito simples.

Adicionar dependência

Em primeiro lugar, você deve adicionar o Jol como uma dependência em seu sistema de construção de código favorito. Eu uso o sbt.

libraryDependencies += "org.openjdk.jol" % "jol-core" % "1.0-SNAPSHOT" % "compile"

Como você deve ter notado, declarei essa dependência como sendo utilizada somente no momento da compilação. Precisamos dela durante o desenvolvimento interativo, mas (ainda) não em tempo de execução.

Comece a explorar

Em seguida, vá até o console sbt e insira estes comandos:

import org.openjdk.jol.info.GraphLayout

println(GraphLayout.parseInstance("USD").toFootprint)
println(GraphLayout.parseInstance("USD").toPrintable)
println(GraphLayout.parseInstance("USD" -> "EUR").totalSize)

Aqui está o resultado que recebo quando inspeciono um valor simples.

scala> println(GraphLayout.parseInstance("USD" -> "EUR").toFootprint)
scala.Tuple2 instance footprint:
 COUNT   AVG   SUM DESCRIPTION
     2    24    48 [C
     2    24    48 java.lang.String
     1    24    24 scala.Tuple2
     5         120 (total)

Na primeira coluna, temos que contar uma instância por classe: duas instâncias do tipo [C ou Array [Char], duas instâncias do tipo String e uma instância do tipo Tuple2.

A terceira coluna mostra a quantidade de memória total que o exemplo de um tipo em particular de dado ocupa. Os arrays de caracteres e as strings ocupam 48 bytes cada. A tuple ocupa 24 bytes. Todos eles se somam a um tamanho total de memória de 120 bytes.

A coluna de média diz qual é a quantidade de memória consumida, em média, por cada instância. Por que tirar essa média, no entanto? Não tenho uma resposta para isso ainda, mas eu presumo que seja uma regra de alinhamento de bytes.

Outra pergunta poderia ser: por que uma única String neste exemplo ocupa 24 bytes? Esse é o momento onde outro utilitário, chamado ClassLayout, vem a calhar:

scala> println(ClassLayout.parseClass(classOf[String]).toPrintable)
java.lang.String object internals:
 OFFSET  SIZE   TYPE DESCRIPTION                    VALUE
      0    12        (object header)                N/A
     12     4 char[] String.value                   N/A
     16     4    int String.hash                    N/A
     20     4    int String.hash32                  N/A
Instance size: 24 bytes (estimated, the sample instance is not available)
Space losses: 0 bytes internal + 0 bytes external = 0 bytes total

A saída diz precisamente como os campos do objeto deverão ser colocados para fora na memória do computador e quanto espaço eles ocupam.

O primeiro item, o cabeçalho do objeto, contém informações sobre o tipo de classe. Ela começa no deslocamento 0 (zero) e ocupa 12 bytes.

O segundo item é o primeiro campo do objeto. Nesse caso, temos um array de caracteres, que é usado para armazenar os caracteres reais que compõem a cadeia. Ela começa no deslocamento 12 e ocupa 4 bytes. Semelhante para os dois campos seguintes.

Se quiser saber mais, a árvore do código-fonte do projeto contém um diretório de exemplos demonstrando algumas de suas características. É um bom lugar para aprender mais sobre Jol.

***

Artigo traduzido pela Redação iMasters, com autorização do autor. Publicado originalmente em http://igstan.ro/posts/2014-09-23-calculating-an-object-graphs-size-on-the-jvm.html