Melhorando a serializacao de objetos imutaveis

Criei um issue no JIRA do JBoss Serialization para melhorar a serialização de objetos imutáveis. Enquanto fazia algumas otimizações em um sistema que utiliza RMI – como protocolo de comunicação com um EJB remoto -, percebi que as chamadas problemáticas eram grandes somente porque continham diversos objetos imutáveis semanticamente iguais.

É muito comum isso ocorrer em função de como geramos instâncias dos tipos básicos na maior parte dos casos. Provavelmente as três maiores fontes de dados hoje são dados entrados pelo usuário, arquivos/documentos de terceiros e dados do banco. Em todos os três casos, as APIs e soluções de parsing utilizadas em quase sua totalidade não fazem cache de instâncias, até porque isso normalmente é desnecessário e sujeito a erros.

Por exemplo, se você estiver usando o Hibernate para ler o bean Produto que, entre outros atributos, possui um atributo BigDecimal que representa a quantidade em estoque e houver 1000 produtos que possuem um estoque de 5.5 unidades 1000 instâncias de BigDecimal 5.5 serão criadas em RAM. Em termos de memória isso não é normalmente relevante, mas quando se tenta serializar mil instâncias ao invés de uma instáncia repetida 1000 vezes, a diferença é brutal:


import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.ObjectOutputStream;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;

public class BigDecimalSerializationTest {
   public static void main(String[] args) throws IOException {
      List instances = new ArrayList();

      for (int i = 0; i < 1000; i++) {
         instances.add(new BigDecimal("5.5"));
      }

      imprimeTamanhoSerializado(instances);

      Collections.fill(instances, new BigDecimal("5.5"));

      imprimeTamanhoSerializado(instances);
   }

   private static void imprimeTamanhoSerializado(final Collection instances)
         throws IOException {
      ByteArrayOutputStream baos = new ByteArrayOutputStream();
      ObjectOutputStream oos = new ObjectOutputStream(baos);

      for (Iterator i = instances.iterator(); i.hasNext();) {
         oos.writeObject(i.next());
      }

      oos.close();

      System.out.println(baos.size());
   }
}

A saída na minha máquina para o teste acima é:


48242
5285

No caso específico que estava otimizando, após considerar o uso de um aspecto, descobri que a maior parte das instâncias era gerado num processo de parsing manual de um arquivo. O simples uso de um Map<String, BigDecimal> e afins para cache das instâncias foi suficiente para eliminar a repetição e trazer um ganho de até 60% em alguns casos.

Contudo, uma otimização assim deveria estar implementada na API de serialização e por isso abri esse issue no JBoss Serialization, que já é superior a API de serialização padrão com respeito ao tamanho em bytes gerado.

One Response to “Melhorando a serializacao de objetos imutaveis”

  1. Eu achei bem legal essa ideia do Michael.

    Vou estar trabalhando em cima disso para a proxima versao.