Il pooling di oggetti è una tecnica obsoleta?


62

Conosco molto bene il concetto di pooling di oggetti e cerco sempre di utilizzarlo il più possibile.

Inoltre, ho sempre pensato che il pool di oggetti fosse la norma standard poiché ho osservato che Java stesso e gli altri framework usano il pool il più possibile.

Di recente però ho letto qualcosa di completamente nuovo (e controintuitivo?) Per me.

Tale pool effettivamente peggiora le prestazioni del programma, specialmente nelle applicazioni simultanee, ed è consigliabile istanziare gli newoggetti, poiché nelle JVM più recenti, l'istanza di un oggetto è molto veloce.

Ho letto questo nel libro: Java Concurrency in Practice

Ora sto cominciando a pensare se sto fraintendendo qualcosa qui dato che la prima parte del libro ha consigliato di usare Executorsquel riutilizzo Threadinvece di creare nuove istanze.

Oggi il pool di oggetti è diventato obsoleto?

Risposte:


72

È deprecato come tecnica generale, perché - come hai notato - la creazione e la distruzione di oggetti di breve durata in sé (ovvero allocazione della memoria e GC) è estremamente economica nelle moderne JVM. Pertanto, l'utilizzo di un pool di oggetti scritto a mano per gli oggetti run-of-the-mill è probabilmente più lento, più complicato e più soggetto a errori rispetto al semplice new. *

Tuttavia ha ancora i suoi usi, per oggetti speciali la cui creazione è relativamente costosa, come connessioni DB / rete, thread ecc.

* Una volta ho dovuto migliorare le prestazioni di un'app Java a scansione. L'indagine ha rivelato un tentativo di utilizzare un pool di oggetti per allocare milioni di oggetti ... e il ragazzo intelligente che lo ha scritto ha usato un unico blocco globale per renderlo sicuro. La sostituzione del pool con plain ha newreso l'app 30 volte più veloce.


1
Quindi, come si può decidere se l'istanza di un oggetto è troppo costosa?
user10326

3
Se l'oggetto consuma risorse del sistema operativo (thread, I / O, memoria condivisa, ecc.)
kevin cline,

13
@ user10326, mediante misurazione :-) Se la creazione dei tuoi oggetti richiede molto tempo e / o sono associati a qualche risorsa speciale, potenzialmente limitata, non di memoria, puoi prendere in considerazione il pooling.
Péter Török,

8
@ user10326, IMO in oltre il 95% dei casi, i criteri di cui sopra rendono facile decidere in anticipo se è necessario un pool di oggetti. (Inoltre, in quasi tutti i casi che richiedono un pool, molto probabilmente userete una libreria / framework esistente, che probabilmente ha già il pool di oggetti implementato per voi.) Per il resto, è comunque facile nascondere la creazione di oggetti in es. una fabbrica, che può essere successivamente reimplementata come meglio credi.
Péter Török,

2
Punto molto importante fatto da @Peter Torok: molti framework e librerie implementano il pooling per te, SEMPRE assicurati che non stai già utilizzando una libreria in pool prima di implementare la tua.
hromanko,

36

La risposta alla domanda concreta: "Il pooling di oggetti è una tecnica deprecata?" è:

No. Il pool di oggetti è ampiamente utilizzato in luoghi specifici - pool di thread, pool di connessioni al database ecc.

La creazione di oggetti generici non è mai stata un processo lento. Il pooling in sé consuma risorse: memoria e potenza di elaborazione. Qualsiasi ottimizzazione è un compromesso.

La regola è:

L'ottimizzazione prematura è un male !!!

Ma quando una determinata ottimizzazione è prematura?

L'ottimizzazione prematura è qualsiasi ottimizzazione effettuata, prima di aver scoperto un collo di bottiglia attraverso una profilazione approfondita .


2
Infatti. OP ha detto "Cerco sempre di usarlo il più possibile" - questo è il problema, IMO.
nerdytenor,

@Boris, quindi secondo la tua seconda frase, non dovremmo obiettare connessioni e thread del pool db fino a quando non li scopriamo come un collo di bottiglia tramite la profilazione?
Pacerier,

1
@Pac Alcuni risultati di profilazione non necessitano di una nuova misurazione costante :-)
David Bullock,

9

Nelle situazioni in cui si desidera evitare completamente la garbage collection, penso che il pool di oggetti sia l'unica alternativa possibile. Quindi no, non è assolutamente una tecnica deprecata.


1
E aggiungerei che è una buona idea evitare GC ogni volta che gli oggetti hanno una vita abbastanza lunga da passare alla generazione precedente.
Zan Lynx,

8

Misurare

Dipende completamente dal tuo caso d'uso, dalle dimensioni dei tuoi oggetti, dalla tua JVM, dalle tue opzioni JVM, dal GC che hai abilitato e da tutta una serie di altri fattori.

In breve: misuralo prima e misuralo dopo. Supponendo che tu stia utilizzando un framework di pool di oggetti (come da Apache), non dovrebbe essere troppo doloroso scambiare tra le implementazioni.

Suggerimento per test di prestazioni extra: lascia che la JVM si riscaldi un po 'prima, esegui i test su una JVM in esecuzione diverse volte, può comportarsi in modo diverso.


3
"Lascia che la JVM si riscaldi un po 'prima", ricordo quando l'unica cosa che doveva essere "riscaldata" era il monitor. Oy, tutto ciò che è nuovo è di nuovo vecchio.
kylben,

L'unica cosa di cui ho bisogno per scaldarmi è il caffè!
Disilluso,

@Marijn, come si fa a "scaldarsi"?
Pacerier,

Vedi il Framework JMH per una spiegazione completa ( openjdk.java.net/projects/code-tools/jmh ) ma in pratica devi dare alla JVM la possibilità di JIT il tuo codice, eseguire GC prima del tuo benchmark e così via.
Martijn Verburg,

8

Tale pool effettivamente peggiora le prestazioni del programma, specialmente nelle applicazioni simultanee, ed è consigliabile creare un'istanza di nuovi oggetti, poiché nelle JVM più recenti, l'istanza di un oggetto è molto veloce.

Dipende dal contesto.


1
Risposta eccellente e convincente. Aggiungerei (forse, forse come un asterisco?) Che l'affermazione "24 byte" si riferisce a 4 istanze di float a 4 byte (16 byte), più 4 byte per il riferimento all'oggetto, più 4 byte per un riferimento di blocco. Questo è esattamente il sovraccarico che il tuo progetto elimina.
strickli,

5

Non so se ci sia una tendenza in evoluzione qui, ma sarà certamente il caso che dipende . Se la tua classe Java gestisce una risorsa esterna, come una connessione RMI o il caricamento di un file di risorse, ecc., Sicuramente i costi per la creazione di istanze di oggetti possono essere ancora elevati (sebbene tali risorse possano essere già raggruppate per te!). Come pratica generale sono d'accordo con il libro.


Bene, ora non lo so. Perché anche in questo caso descrivi quale (prima di leggere questo) utilizzerei sicuramente il pool, avrei anche un overhead.1) Nuovi costrutti per gestire il pool 2) Sincronizzazione per ottenere / rilasciare l'oggetto dal pool 3) mantenimento del pool ecc. Quindi ora sto pensando che forse non c'è nessun caso d'uso che sia utile se non per esempio memorizzare nella cache un socket invece di aprirne uno nuovo ogni volta per connettersi al server. E questo caso è dovuto alla rete sovraccarico di creazione di latenza e non istanziazione
user10326

@ user10326 Sì, esattamente. Vedo aprire un socket come parte del sovraccarico dell'istanza, se è il compito della classe a farlo e deve essere inizializzato nel costruttore, allora la latenza e gli impatti di IO sono ciò di cui ti preoccupi.
Jeremy,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.