O pool de objetos é uma técnica obsoleta?

62

Estou muito familiarizado com o conceito de pool de objetos e sempre tento usá-lo o máximo possível.

Além disso, sempre achei que o pool de objetos é a norma padrão, como observei que o próprio Java e as outras estruturas usam o pool o máximo possível.

Recentemente, embora eu tenha lido algo completamente novo (e contra-intuitivo?) Para mim.

Esse pool realmente piora o desempenho do programa, especialmente em aplicativos simultâneos, e é aconselhável instanciar newobjetos, pois nas JVMs mais recentes, a instanciação de um objeto é realmente rápida.

Eu li isso no livro: Java Concurrency in Practice

Agora estou começando a pensar se estou entendendo algo errado desde a primeira parte do livro, aconselhada a usar Executorsessas reutilizações em Threadvez de criar novas instâncias.

Então, o pool de objetos ficou obsoleto hoje em dia?

user10326
fonte

Respostas:

72

Ele foi descontinuado como uma técnica geral, porque - como você notou - a criação e a destruição de objetos de vida curta em si (por exemplo, alocação de memória e GC) são extremamente baratas nas JVMs modernas. Portanto, o uso de um pool de objetos manuscritos para seus objetos comuns é provavelmente mais lento, mais complicado e mais suscetível a erros do que comum new. *

No entanto, ele ainda tem seus usos, para objetos especiais cuja criação é relativamente cara, como conexões de banco de dados / rede, threads etc.

* Uma vez eu tive que melhorar o desempenho de um aplicativo Java de rastreamento. A investigação descobriu uma tentativa de usar um pool de objetos para alocar milhões de objetos ... e o esperto que o escreveu usou um único bloqueio global para torná-lo seguro. Substituir a piscina por simples newtornou o aplicativo 30 vezes mais rápido.

Péter Török
fonte
1
Então, como alguém pode decidir se a instanciação de um objeto é muito cara?
user10326
3
Se o objeto consome recursos do sistema operacional (threads, I / O, memória compartilhada, etc.)
kevin Cline
13
@ user10326, por medição :-) Se a criação de seus objetos levar muito tempo e / ou estiverem associados a algum recurso especial, potencialmente limitado, sem memória, considere o pool.
Péter Török
8
@ user10326, IMO em mais de 95% dos casos, os critérios acima tornam fácil decidir com antecedência se você precisa de um pool de objetos. (Além disso, em quase todos os casos que precisam de um pool, você provavelmente usará uma biblioteca / estrutura existente, que provavelmente já possui o pool de objetos implementado para você.) Quanto ao restante, ainda é fácil ocultar a criação de objetos, por exemplo. uma fábrica, que pode ser reimplementada posteriormente da maneira que você achar melhor.
Péter Török
2
Ponto muito importante feito por Peter Torok: muitas estruturas e bibliotecas implementam o pool para você, SEMPRE verifique se você ainda não está usando uma biblioteca em pool antes de implementar a sua própria.
Hromanko 19/10/11
36

A resposta para a pergunta concreta: 'O pool de objetos é uma técnica obsoleta?' é:

Não. O pool de objetos é amplamente usado em locais específicos - pool de threads, pool de conexão de banco de dados etc.

A criação geral de objetos nunca foi um processo lento. O pool em si consome recursos - memória e poder de processamento. Qualquer otimização é uma troca.

A regra é:

Otimização prematura é má !!!

Mas quando uma otimização é prematura?

Otimização prematura é qualquer otimização feita antes de você descobrir um gargalo por meio de criação de perfil completo .

Boris Yankov
fonte
2
De fato. OP disse: "Eu sempre tento usá-lo o máximo possível" - este é o problema, IMO.
Nerdytenor 20/10
@ Boris, portanto, de acordo com sua segunda frase, não devemos objetar conexões e threads db de pool até os descobrirmos como um gargalo via criação de perfil?
Pacerier 10/05
1
@Pac Alguns resultados de perfil não precisa re-medir :-) constante
David Bullock
9

Nas situações em que você deseja evitar a coleta de lixo completamente, acho que o pool de objetos é a única alternativa viável. Portanto, não, absolutamente não é uma técnica obsoleta.

Jer
fonte
1
E eu acrescentaria que é uma boa idéia evitar o GC sempre que os objetos tiverem vida longa o suficiente para serem transferidos para a geração mais antiga.
Zan Lynx
8

A medida

Depende completamente do seu caso de uso, tamanho de seus objetos, sua JVM, suas opções de JVM, qual GC você ativou e vários outros fatores.

Resumindo: meça antes e depois. Supondo que você esteja usando uma estrutura de pool de objetos (como no Apache), não deve ser muito doloroso alternar entre implementações.

Dica de teste de desempenho extra - deixe a JVM esquentar um pouco primeiro, execute os testes em uma JVM em execução várias vezes, pois ela pode se comportar de maneira diferente.

Martijn Verburg
fonte
3
"deixe a JVM esquentar um pouco primeiro" - lembro-me de quando a única coisa que precisava ser "aquecida" era o monitor. Oi, tudo novo é velho novamente.
Kylben
A única coisa que eu preciso para aquecer é o café!
Disillusioned
@ Marijn, como você deixa "aquecer"?
Pacerier
Veja o JMH Framework para obter uma explicação completa ( openjdk.java.net/projects/code-tools/jmh ), mas basicamente você precisa dar à JVM a chance de JIT seu código, execute os GC antes do seu benchmark e assim por diante.
Martijn Verburg
8

Esse pool realmente piora o desempenho do programa, especialmente em aplicativos simultâneos, e é recomendável instanciar novos objetos, pois nas JVMs mais recentes, a instanciação de um objeto é muito rápida.

Depende do contexto.


fonte
1
Excelente resposta e convincente. Eu acrescentaria (talvez, talvez como um asterisco?) Que a declaração "24 bytes" se refira a 4 instâncias de flutuações de 4 bytes (16 bytes), mais 4 bytes para a referência do objeto e mais 4 bytes para uma referência de bloqueio. Essa é a sobrecarga exata que seu design elimina.
Strickli 4/17
5

Não sei se há uma tendência de mudança aqui, mas certamente será o caso de que depende . Se sua classe Java estiver gerenciando um recurso externo, como uma conexão RMI ou carregando um arquivo de recurso, etc. - certamente os custos de instanciação de objetos ainda poderão ser altos (embora esses recursos já possam estar reunidos para você!). Como prática geral, eu concordo com o livro.

Jeremy
fonte
Bem, agora eu não sei. Porque, mesmo neste caso, você descreve quais (antes de ler isso) eu definitivamente usaria o pooling, também teria sobrecarga.1) Novas construções para lidar com o pooling 2) Sincronização para o objeto de obtenção / liberação do pool 3) manutenção do pool etc. Então, agora estou pensando que talvez não exista um caso de uso que seja útil, exceto, por exemplo, armazenar em cache um soquete em vez de abrir um novo a cada vez para conectar-se ao servidor. E esse caso é devido à rede sobrecarga de criação de latência e não instanciação
user10326 19/10/11
@ user10326 Sim exatamente. Vejo a abertura de um soquete como parte da sobrecarga da instanciação, se é o trabalho da classe fazer isso e deve ser inicializado no construtor, então os impactos de latência e IO são o que você está preocupado.
Jeremy