Dovremmo evitare la creazione di oggetti in Java?


243

Un collega mi ha detto che nella creazione di oggetti Java è l'operazione più costosa che potresti eseguire. Quindi posso solo concludere di creare il minor numero possibile di oggetti.

Questo sembra in qualche modo vanificare lo scopo della programmazione orientata agli oggetti. Se non stiamo creando oggetti, stiamo solo scrivendo uno stile di classe C lungo, per l'ottimizzazione?


39
"La creazione di oggetti Java è l'operazione più costosa che potresti eseguire" . Ti dispiace condividere la fonte di tale affermazione?
Songo,

92
E - operazione più costosa rispetto a cosa? Rispetto al calcolo di pi a 900 cifre o rispetto all'incremento di un int?
Jasonk,

4
Potrebbero aver parlato di una parte particolare della creazione di quell'oggetto specifico? Sto pensando a come Apple utilizza una coda per tableViewCells. Forse il tuo collega stava suggerendo di creare alcuni oggetti e riutilizzarli a causa di un sovraccarico associato a quegli oggetti specifici? Sto solo cercando di capire perché abbiano fatto una simile affermazione.
Tyler DeWitt,

3
Questa è una delle cose più divertenti che abbia mai sentito parlare di programmazione.)
Mert Akcakaya,

22
Anche l'aritmatica è piuttosto costosa; dovremmo evitare i calcoli, oh, e l'avvio di una JVM è davvero costoso, quindi non dovremmo avviare alcuna JVM :-).
James Anderson,

Risposte:


478

Il tuo collega non ha idea di cosa stia parlando.

La tua operazione più costosa sarebbe ascoltarli . Hanno sprecato il tuo tempo a indirizzarti erroneamente verso informazioni obsolete di oltre un decennio (alla data originale in cui è stata pubblicata la risposta) , oltre a dover dedicare del tempo a pubblicare qui e ricercare la verità su Internet.

Spero che stiano semplicemente rigurgitando con ignoranza qualcosa che hanno sentito o letto da più di un decennio fa e che non conoscono meglio. Prenderei qualsiasi altra cosa che dicono anche come sospetta, questo dovrebbe essere un errore ben noto da chiunque si tenga aggiornato in entrambi i modi.

Tutto è un oggetto (tranne primitives)

Tutto tranne i primitivi ( int, long, double, ecc.) Sono oggetti in Java. Non è possibile evitare la creazione di oggetti in Java.

La creazione di oggetti in Java grazie alle sue strategie di allocazione della memoria è più veloce di C ++ nella maggior parte dei casi e per tutti gli scopi pratici rispetto a qualsiasi altra cosa nella JVM può essere considerata "libera" .

Già alla fine degli anni 2000, le implementazioni JVM avevano un certo sovraccarico di prestazioni nell'attuale allocazione degli oggetti. Questo non è stato il caso almeno dal 2005.

Se ci si sintonizza -Xmsper supportare tutta la memoria necessaria per il corretto funzionamento dell'applicazione, il GC potrebbe non dover mai eseguire e spazzare la maggior parte della spazzatura nelle moderne implementazioni del GC, i programmi di breve durata potrebbero non GC affatto.

Non cerca di massimizzare lo spazio libero, che è comunque un'aringa rossa, massimizza le prestazioni del runtime. Se ciò significa che l'heap JVM è allocato quasi sempre al 100%, così sia. La memoria heap gratuita di JVM non ti dà comunque niente di tutto ciò che stai seduto lì.

C'è un malinteso sul fatto che il GC libererà la memoria sul resto del sistema in modo utile, questo è completamente falso!

L'heap JVM non cresce e si restringe in modo che il resto del sistema sia influenzato positivamente dalla memoria libera nell'heap JVM . -Xmsalloca TUTTO ciò che viene specificato all'avvio e la sua euristica è di non rilasciare mai realmente tutta quella memoria al sistema operativo per condividerla con qualsiasi altro processo del sistema operativo fino a quando quell'istanza di JVM non si chiude completamente. -Xms=1GB -Xmx=1GBalloca 1 GB di RAM indipendentemente dal numero di oggetti effettivamente creati in un determinato momento. Esistono alcune impostazioni che consentono il rilascio delle percentuali della memoria dell'heap, ma per tutti gli scopi pratici la JVM non è mai in grado di rilasciare abbastanza memoria per consentire che ciò avvengaquindi nessun altro processo può recuperare questa memoria, quindi il resto del sistema non beneficia neppure dell'heap JVM. Una RFE per questo è stata "accettata" il 29-NOV-2006, ma non è mai stato fatto nulla al riguardo. Questo comportamento non è considerato una preoccupazione da nessuno di autorità.

C'è un malinteso sul fatto che la creazione di molti piccoli oggetti di breve durata causa la sospensione della JVM per lunghi periodi di tempo, anche questo è falso.

Gli attuali algoritmi GC sono effettivamente ottimizzati per la creazione di molti piccoli oggetti di breve durata, che in pratica è l'euristica del 99% per gli oggetti Java in ogni programma. I tentativi di raggruppamento di oggetti in realtà peggioreranno le prestazioni della JVM nella maggior parte dei casi.

Gli unici oggetti che devono essere raggruppati oggi sono Oggetti che fanno riferimento a risorse limitate esterne alla JVM; Socket, file, connessioni al database, ecc. E possono essere riutilizzati. Gli oggetti regolari non possono essere raggruppati nello stesso senso delle lingue che consentono l'accesso diretto alle posizioni di memoria. La memorizzazione nella cache degli oggetti è un concetto diverso e può o meno essere ciò che alcune persone ingenuamente chiamano pool , i due concetti non sono la stessa cosa e non dovrebbero essere confusi.

I moderni algoritmi GC non hanno questo problema perché non si allocano secondo una pianificazione, ma si allocano quando è necessaria memoria libera in una determinata generazione. Se l'heap è abbastanza grande, allora non si verificano deallocazioni abbastanza a lungo da causare pause.

I linguaggi dinamici orientati agli oggetti battono C anche adesso nei test sensibili al calcolo.


275
+1: "La tua operazione più costosa sarebbe ascoltarli ...". La migliore che abbia sentito da tempo.
rjnilsson,

21
@DeadMG, anche con il sovraccarico GC cumulativo, Java può essere più veloce di C ++ (ad esempio, a causa della compattazione dell'heap, riducendo al minimo le mancate cache per determinate strutture di dati).
SK-logic,

13
@ SK-logic: Dato che il GC non è deterministico, nella migliore delle ipotesi è estremamente difficile dimostrarlo. Per quanto riguarda la riduzione al minimo degli errori nella cache, è difficile farlo quando ogni oggetto deve essere un'altra indiretta, sprecando spazio nella cache e aumentando i tempi di esecuzione. Tuttavia, sostengo che semplicemente usando un allocatore appropriato in C ++ come pool di oggetti o arena di memoria, puoi facilmente abbinare o battere le prestazioni del garbage collector. Ad esempio, è possibile scrivere una classe di arena di memoria che eseguirà più velocemente di _alloca, ammortizzata.
DeadMG

33
La creazione di oggetti ora è più economica di prima. Non è gratuito però. Chiunque ti dica che sta mentendo. E il link sulle lingue OO che battono C è una risposta esagerata a qualcuno che sta davvero cercando di imparare.
Jasonk,

17
È a causa di questo tipo di risposte che finiamo con un codice scadente. La risposta giusta è creare un oggetto in Java sia creare l'oggetto Java che inizializzarlo. La prima parte è economica, la seconda può essere molto costosa. Le persone dovrebbero sempre guardare a ciò che accade nei costruttori prima di utilizzare la newparola chiave in un punto attivo. Ho visto persone usare new ImageIcon(Image)il paint()metodo degli oggetti Swing, che è piuttosto costoso e rendeva l'intera UI super lenta. Quindi non è una risposta in bianco e nero, pensa prima di usare newda qualche parte.
qwertzguy,

94

Bottom line: non compromettere il tuo design al fine di prendere scorciatoie per creare oggetti. Evitare di creare oggetti inutilmente. Se sensato, progettare per evitare operazioni ridondanti (di qualsiasi tipo).

Contrariamente alla maggior parte delle risposte: sì, l'allocazione degli oggetti ha un costo associato. È a basso costo, ma dovresti evitare di creare oggetti non necessari. Come dovresti evitare qualsiasi cosa non necessaria nel tuo codice. I grafici di oggetti di grandi dimensioni rendono il GC più lento, implicano tempi di esecuzione più lunghi in quanto probabilmente avrai più chiamate di metodo, innescano più mancate cache della CPU e aumentano la probabilità che il tuo processo venga scambiato su disco in casi di RAM bassa.

Prima che qualcuno volesse affermare che si trattava di un caso limite: ho creato applicazioni di profilatura che, prima dell'ottimizzazione, hanno creato oltre 20 MB di oggetti per elaborare circa 50 righe di dati. Questo va bene nei test, fino a quando non si scalano fino a cento richieste al minuto e all'improvviso si creano 2 GB di dati al minuto. Se vuoi fare 20 reqs / sec stai creando 400 MB di oggetti e poi buttandoli via. 20 reqs / sec sono minuscoli per un server decente.


7
Posso aggiungere un esempio: In alcuni casi si fa davvero nessuna differenza in termini di chiarezza del codice, ma posso fare una più o meno grande differenza in termini di prestazioni: Quando si legge da corsi d'acqua, per esempio, non è raro per usare qualcosa di simile while(something) { byte[] buffer = new byte[10240]; ... readIntoBuffer(buffer); ...che può essere dispendioso rispetto ad es byte[] buffer = new byte[10240]; while(something) { ... readIntoBuffer(buffer); ....
JimmyB,

4
+1: la creazione di oggetti non necessari (o meglio la pulizia dopo la rimozione) può a volte essere costosa. La grafica 3D / il codice OpenGL in Java è un posto in cui ho visto delle ottimizzazioni fatte per ridurre al minimo il numero di oggetti creati poiché GC potrebbe altrimenti causare il caos con il tuo framerate.
Leone,

1
Wow! 20+ MB per elaborare 50 righe di dati? Sembra pazzo. In ogni caso, quegli oggetti durano a lungo? Perché questo è il caso che avrebbe importanza per GC. Se d'altra parte stai solo discutendo dei requisiti di memoria , questo non è correlato alla garbage collection o all'efficienza della creazione di oggetti ...
Andres F.

1
Gli oggetti hanno vita breve (meno di 0,5 secondi nel caso generale). Quel volume di immondizia influisce ancora sulle prestazioni.
Jasonk,

2
Grazie per essere realmente fermi sulla realtà, chiunque viva con gli effetti di un'architettura sconsiderata può relazionarsi moltissimo.
JM Becker,

60

In realtà, a causa delle strategie di gestione della memoria rese possibili dal linguaggio Java (o da qualsiasi altro linguaggio gestito), la creazione di oggetti è poco più che incrementare un puntatore in un blocco di memoria chiamato generazione giovane. È molto più veloce di C, dove deve essere effettuata una ricerca di memoria libera.

L'altra parte del costo è la distruzione degli oggetti, ma è difficile confrontarla con C. Il costo di una raccolta si basa sulla quantità di oggetti salvati a lungo termine, ma la frequenza delle raccolte si basa sulla quantità di oggetti creati ... in alla fine, è ancora molto più veloce della gestione della memoria in stile C.


7
+1 Anche se il Garbage Collector "funziona", ogni programmatore Java dovrebbe conoscere il Garbage Collector generazionale.
benzado,

18
Le versioni più recenti di Java possono eseguire analisi di escape, il che significa che può allocare memoria per oggetti che non sfuggono a un metodo nello stack, in modo che la loro pulizia sia gratuita: il garbage collector non deve occuparsi di quegli oggetti , vengono automaticamente scartati quando il frame dello stack del metodo viene srotolato (quando il metodo ritorna).
Jesper,

4
Un passo successivo per l'analisi di escape è quello di "allocare" la memoria per piccoli oggetti puramente nei registri (ad esempio un Pointoggetto potrebbe rientrare in 2 registri di uso generale).
Joachim Sauer,

6
@Joachim Sauer: Questo è un po 'quello che viene fatto nelle nuove implementazioni della VM HotSpot. Si chiama sostituzione scalare.
someguy,

1
@someguy: ne ho letto qualche tempo fa come la prossima cosa, ma non ho seguito per verificare se è già stato fatto. È un'ottima notizia sapere che abbiamo già questo.
Joachim Sauer,

38

Altri poster hanno giustamente sottolineato che la creazione di oggetti è estremamente veloce in Java e che di solito non dovresti preoccuparti in nessuna normale applicazione Java.

Ci sono un paio di situazioni molto speciali in cui è una buona idea evitare la creazione di oggetti.

  • Quando si scrive un'applicazione sensibile alla latenza e si desidera evitare pause GC. Più oggetti produci, più GC si verifica e maggiore è la possibilità di pause. Questa potrebbe essere una considerazione valida per i giochi, alcune applicazioni multimediali, il controllo robotico, il trading ad alta frequenza, ecc. La soluzione è pre-allocare tutti gli oggetti / array necessari in anticipo e riutilizzarli. Esistono librerie specializzate nel fornire questo tipo di funzionalità, ad esempio Javolution . Ma probabilmente, se ti interessa davvero la bassa latenza, dovresti usare C / C ++ / assembler piuttosto che Java o C # :-)
  • Evitare le primitive inscatolate (doppie, intere ecc.) Può essere una micro-ottimizzazione molto utile in determinate circostanze. Poiché le primitive unboxed (double, int ecc.) Evitano il sovraccarico per oggetto, sono molto più veloci in termini di elaborazione intensiva della CPU come l'elaborazione numerica ecc. In genere, le matrici primitive funzionano molto bene in Java, quindi si desidera utilizzarle per il crunching dei numeri piuttosto che qualsiasi altro tipo di oggetto.
  • In situazioni di memoria limitata, si desidera ridurre al minimo il numero di oggetti (attivi) creati poiché ogni oggetto comporta un piccolo sovraccarico (in genere 8-16 byte a seconda dell'implementazione JVM). In tali circostanze, è consigliabile preferire un numero ridotto di oggetti o matrici di grandi dimensioni per memorizzare i dati piuttosto che un numero elevato di piccoli oggetti.

3
Vale la pena notare che l'analisi di escape consentirà di archiviare nello stack oggetti di breve durata (vita limitata), questi oggetti possono essere deallocati gratuitamente ibm.com/developerworks/java/library/j-jtp09275/index.html
Richard Tingle

1
@Richard: ottimo punto - l'analisi dell'evasione fornisce alcune ottime ottimizzazioni. Bisogna stare attenti a fare affidamento su di esso però: non è garantito che accada in tutte le implementazioni Java e in tutte le circostanze. Quindi devi essere spesso un benchmark per essere sicuro.
Mikera,

2
Inoltre, un'analisi di fuga corretta al 100% equivale al problema di arresto. Non fare affidamento sull'analisi di escape in situazioni complesse, poiché è probabile che fornisca la risposta errata almeno qualche volta.
Jules il

Il primo e l'ultimo punto non si applicano ai piccoli oggetti che vengono rilasciati rapidamente, muoiono in eden (pensate allo stack stack in C, praticamente gratuito) e non influenzano mai i tempi GC o l'allocazione di memoria. La maggior parte dell'allocazione di oggetti in Java rientra in questa categoria ed è quindi essenzialmente gratuita. Il secondo punto è ancora valido.
Bill K,

17

C'è un nocciolo di verità in ciò che dice il tuo collega. Suggerisco rispettosamente che il problema con la creazione di oggetti è in realtà la garbage collection . In C ++ il programmatore può controllare con precisione come viene allocata la memoria. Il programma può accumulare greggio per tutto il tempo che desidera. Inoltre, il programma C ++ può scartare il crud usando un thread diverso da quello che lo ha creato. Quindi il thread attualmente funzionante non deve mai smettere di ripulire.

Al contrario, Java Virtual Machine (JVM) interrompe periodicamente il codice per recuperare memoria non utilizzata. La maggior parte degli sviluppatori Java non nota mai questa pausa, poiché di solito è poco frequente e molto breve. Più greggio accumuli o più la tua JVM è vincolata, più frequenti sono queste pause. È possibile utilizzare strumenti come VisualVM per visualizzare questo processo.

Nelle recenti versioni di Java, è possibile ottimizzare l' algoritmo Garbage Collection (GC) . Come regola generale, più breve si desidera effettuare le pause, più costoso è l'overhead nella macchina virtuale (ovvero CPU e memoria impiegate per coordinare il processo GC).

Quando potrebbe importare? Ogni volta che ti interessano tassi di risposta coerenti al di sotto dei millisecondi, ti occuperai di GC. I sistemi di trading automatici scritti in Java ottimizzano fortemente la JVM per ridurre al minimo le pause. Le aziende che altrimenti scriverebbero Java si rivolgono al C ++ in situazioni in cui i sistemi devono essere altamente reattivi in ​​ogni momento.

Per la cronaca, in genere non perdono l'evitamento degli oggetti! Impostazione predefinita per la programmazione orientata agli oggetti. Regola questo approccio solo se il GC ti ostacola, e solo dopo aver provato a mettere a punto la JVM per mettere in pausa per meno tempo. Un buon libro sull'ottimizzazione delle prestazioni Java è Java Performance di Charlie Hunt e Binu John.


10
La maggior parte dei moderni JVM supporta algoritmi di garbage collection migliori rispetto a "stop the world". Il tempo ammortizzato trascorso nella garbage collection è spesso inferiore a quello che viene speso esplicitamente chiamando malloc / free. Inoltre la gestione della memoria C ++ viene eseguita in un thread separato?

10
Le versioni Java più recenti di Oracle possono eseguire analisi di escape, il che significa che gli oggetti che non sfuggono a un metodo vengono allocati nello stack, il che li rende puliti gratuitamente - vengono automaticamente deallocati quando il metodo ritorna.
Jesper,

2
@ ThorbjørnRavnAndersen: Davvero? Intendi davvero GC senza pausa o intendi GC di solito "parallelo-ma-a-volte-un po 'in pausa"? Non capisco come il GC non possa mai mettere in pausa un programma, eppure spostare gli oggetti allo stesso tempo ... mi sembra, letteralmente, impossibile ...
Mehrdad,

2
@ ThorbjørnRavnAndersen: come può "fermare il mondo" ma mai "mettere in pausa la JVM"? Non ha senso ...
Mehrdad,

2
@ ThorbjørnRavnAndersen: No, non lo so; Non capisco cosa stai dicendo. O il GC a volte mette in pausa il programma, nel qual caso non è - per definizione - non "senza pausa", o non mette mai in pausa il programma (quindi, è "senza pausa"), nel qual caso non capisco come corrisponde alla tua affermazione "si ferma il mondo quando non riesce a tenere il passo", poiché ciò sembrerebbe implicare che il programma è in pausa mentre il GC è in funzione. Ti dispiacerebbe spiegare qual è il caso (è senza pausa o no?), E come è possibile?
Mehrdad,


9

il GC è sintonizzato per molti oggetti di breve durata

Detto questo, se puoi ridurre banalmente l'allocazione degli oggetti, dovresti

un esempio sarebbe costruire una stringa in un ciclo, il modo ingenuo sarebbe

String str = "";
while(someCondition){
    //...
    str+= appendingString;
}

che crea un nuovo Stringoggetto su ogni +=operazione (più StringBuilderae il nuovo array di caratteri sottostante)

puoi facilmente riscriverlo in:

StringBuilder strB = new StringBuilder();
while(someCondition){
    //...
    strB.append(appendingString);
}
String str = strB.toString();

questo modello (risultato immutabile e un valore intermedio mutabile locale) può essere applicato anche ad altre cose

ma a parte questo, dovresti cercare un profiler per trovare il vero collo di bottiglia invece di inseguire i fantasmi


il grande vantaggio di questo StringBuilderapproccio è il pre-formato il StringBuildermodo che non deve mai riallocare la matrice sottostante con la StringBuilder(int)costruzione. Questo lo rende una singola allocazione, piuttosto che una 1+Nallocazione.

2
@JarrodRoberson StringBuilder raddoppierà almeno la capacità corrente o in altre parole la capacità aumenterà in modo esponenziale solo per allocazioni log (n)
maniaco del cricchetto,

6

Joshua Bloch (uno dei creatori di piattaforme Java) ha scritto nel suo libro Effective Java nel 2001:

Evitare la creazione di oggetti mantenendo il proprio pool di oggetti è una cattiva idea a meno che gli oggetti nel pool non siano estremamente pesanti. Un esempio prototipico di un oggetto che giustifica un pool di oggetti è una connessione al database. Il costo per stabilire la connessione è sufficientemente elevato da avere senso riutilizzare questi oggetti. In generale, tuttavia, mantenere i propri pool di oggetti ingombra il codice, aumenta il footprint di memoria e danneggia le prestazioni. Le moderne implementazioni JVM dispongono di netturbini altamente ottimizzati che superano facilmente tali pool di oggetti su oggetti leggeri.


1
Anche con cose come la connessione di rete, l'importante non è mantenere gli oggetti allocati dal GC associati alle connessioni, ma piuttosto mantenere l'insieme di connessioni che esistono al di fuori del mondo gestito dal GC. Ci sono momenti in cui il pooling degli oggetti stessi può essere utile; la maggior parte di quelli derivano da casi in cui una coppia di riferimenti allo stesso oggetto può essere semanticamente equivalente a una coppia di riferimenti a oggetti identici ma distinti, ma presentano comunque alcuni vantaggi. Ad esempio, due riferimenti alla stessa stringa di cinquantamila caratteri possono essere controllati per l'uguaglianza ...
supercat

2
... molto più veloce dei riferimenti a due stringhe distinte ma identiche di quella lunghezza.
supercat

5

Dipende molto dall'applicazione specifica, quindi è molto difficile da dire in generale. Tuttavia, sarei piuttosto sorpreso se la creazione di oggetti fosse in realtà un collo di bottiglia delle prestazioni in un'applicazione. Anche se sono lenti, il vantaggio in stile codice probabilmente supererà le prestazioni (a meno che non sia effettivamente evidente per l'utente)

In ogni caso, non dovresti nemmeno iniziare a preoccuparti di queste cose fino a quando non hai profilato il tuo codice per determinare i colli di bottiglia delle prestazioni effettive invece di indovinare. Fino ad allora, dovresti fare tutto ciò che è meglio per la leggibilità del codice, non per le prestazioni.


Nelle prime versioni di Java c'era un costo sostanziale sostenuto quando il garbage collector ripuliva gli oggetti scartati. Le versioni recenti di Java hanno notevolmente migliorato le opzioni GC, quindi la creazione di oggetti raramente è un problema. Di solito i sistemi Web sono limitati da chiamate IO a sistemi esterni a meno che non si stia calcolando qualcosa di veramente complesso sul server delle applicazioni come parte del caricamento della pagina.
Greg

3

Penso che il tuo collega debba aver detto dal punto di vista della creazione di oggetti non necessari. Voglio dire, se stai creando spesso lo stesso oggetto, allora è meglio condividere quell'oggetto. Anche nei casi in cui la creazione di oggetti è complessa e richiede più memoria, potresti voler clonare quell'oggetto ed evitare di creare quel processo di creazione di oggetti complessi (ma ciò dipende dalle tue esigenze). Penso che la frase "La creazione di oggetti è costosa" dovrebbe essere presa nel contesto.

Per quanto riguarda i requisiti di memoria JVM, attendere Java 8, non è nemmeno necessario specificare -Xmx, le impostazioni del meta spazio si occuperanno della necessità di memoria JVM e cresceranno automaticamente.


Sarebbe molto costruttivo se le persone mettessero commenti anche mentre votavano in giù qualsiasi risposta. Dopotutto siamo tutti qui per condividere la conoscenza, e semplicemente giudicare senza una ragione adeguata non servirà a nessuno scopo.
AKS

1
Lo scambio di stack dovrebbe avere un meccanismo per notificare agli utenti che votano verso il basso qualsiasi risposta, quando viene pubblicato un commento su quella risposta.
AKS

1

C'è di più nella creazione di una classe che nell'allocare memoria. C'è anche l'inizializzazione, non sono sicuro del perché quella parte non sia coperta da tutte le risposte. Le classi normali contengono alcune variabili, eseguono una forma di inizializzazione e non è gratuito. A seconda della classe, può leggere file o eseguire qualsiasi altro numero di operazioni lente.

Quindi considera solo cosa fa il costruttore di classi, prima di capire se è gratuito o meno.


2
Una classe ben progettata non dovrebbe fare lavori pesanti nel suo costruttore, proprio per questo motivo. Ad esempio, la classe di lettura dei file potrebbe ridurre il costo dell'istanza verificando solo che il file di destinazione esista all'avvio e rinviando qualsiasi operazione effettiva sul file fino al primo metodo che richiede i dati dal file.
GordonM,

2
Se il costo aggiuntivo viene spostato in "if (firstTime)", ricreare la classe in un ciclo è ancora più costoso del riutilizzo della classe.
Alex,

Stavo per pubblicare una risposta simile e credo che questa sia la risposta giusta. Tante persone sono accecate da quei detti che la creazione di oggetti Java è economica che non si rendono conto che spesso i costruttori o l'ulteriore inizializzazione possono essere tutt'altro che economici. Ad esempio la classe ImageIcon in Swing, anche se gli passi un oggetto Image precaricato, il costruttore è piuttosto costoso. E non sono d'accordo con @GordonM, molte classi del JDK eseguono la maggior parte dell'init nel costruttore e penso che renda il codice più snello, progettato meglio.
qwertzguy,

1

Il GC di Java è in realtà molto ottimizzato in termini di creazione rapida di molti oggetti in modo "esplosivo". Da quello che posso capire usano un allocatore sequenziale (allocatore O (1) più rapido e semplice per richieste di dimensioni variabili) per quel tipo di "ciclo di scoppio" in uno spazio di memoria che chiamano "spazio Eden", e solo se gli oggetti persistono dopo un ciclo GC vengono spostati in un luogo in cui il GC può raccoglierli uno per uno.

Detto questo, se le tue esigenze di prestazione diventano abbastanza critiche (come misurato con i reali requisiti dell'utente finale), gli oggetti comportano un sovraccarico, ma non ci penserei così tanto in termini di creazione / allocazione. Ha più a che fare con la località di riferimento e la dimensione aggiuntiva di tutti gli oggetti in Java, come richiesto per supportare concetti come riflessione e invio dinamico ( Floatè più grande di float, spesso qualcosa come 4 volte più grande su 64 bit con i suoi requisiti di allineamento, e un array di Floatnon è necessariamente garantito per essere memorizzato contiguo da quello che ho capito).

Una delle cose più accattivanti che abbia mai visto sviluppato in Java che mi ha fatto considerare un contendente dei pesi massimi nel mio campo (VFX) è stato un tracciatore di percorsi standard interattivo e multithreading (che non utilizza la cache di irradianza o BDPT o MLS o qualsiasi altra cosa) su la CPU fornisce anteprime in tempo reale che convergono piuttosto rapidamente in un'immagine priva di disturbi. Ho lavorato con professionisti in C ++ dedicando le loro carriere a tali cose con profilatori fantasiosi in mano che hanno avuto difficoltà a farlo.

Ma ho dato un'occhiata al codice sorgente e mentre utilizzava un sacco di oggetti a costi insignificanti, le parti più critiche del tracciatore di tracciati (BVH, triangoli e materiali) evitavano molto chiaramente e deliberatamente oggetti a favore di grandi matrici di tipi primitivi ( per lo più float[]e int[]), che lo ha fatto utilizzare molta meno memoria e garantito la località spaziale per passare da uno floatnell'array al successivo. Non penso che sia troppo speculativo pensarlo, se l'autore ha usato i tipi in scatola piacevaFloatlì, avrebbe comportato un costo piuttosto elevato per le sue prestazioni. Ma stiamo parlando della parte più assolutamente critica di quel motore, e sono abbastanza sicuro, dato l'abilità con cui lo sviluppatore lo ha ottimizzato, che l'ha misurato e applicato quell'ottimizzazione molto, molto giudiziosamente, dal momento che ha usato felicemente oggetti ovunque costo insignificante per il suo impressionante tracciatore di percorsi in tempo reale.

Un collega mi ha detto che nella creazione di oggetti Java è l'operazione più costosa che potresti eseguire. Quindi posso solo concludere di creare il minor numero possibile di oggetti.

Anche in un settore così critico come il mio, non scriverai prodotti efficienti se ti tendi il tendine del ginocchio in posti che non contano. Vorrei anche sostenere che i campi più critici in termini di prestazioni potrebbero comportare una domanda di produttività ancora più elevata, poiché abbiamo bisogno di tutto il tempo extra che possiamo ottenere per mettere a punto quegli hotspot che contano davvero non sprecando così tempo in cose che non lo fanno . Come nell'esempio del tracciatore di percorsi dall'alto, l'autore ha applicato abilmente e con giudizio queste ottimizzazioni solo ai luoghi che contano veramente, veramente, e probabilmente con il senno di poi dopo aver misurato, e che comunque hanno usato felicemente oggetti ovunque.


1
Il tuo primo paragrafo è sulla buona strada. Gli spazi di eden e young sono collezionisti di copie che hanno ca. 0 costo per oggetti morti. Consiglio vivamente questa presentazione . Da un lato, ti rendi conto che questa domanda è del 2012, giusto?
JimmyJames,

@JimmyJames Mi annoio e mi piace vagliare le domande, anche quelle vecchie. Spero che alla gente non dispiaccia la mia negromanzia! MrGreen
Dragon Energy il

@JimmyJames Non è più il caso che i tipi in box abbiano requisiti di memoria aggiuntivi e non siano garantiti contigui in un array? Tendo a pensare gli oggetti sono sporcizia a buon mercato in Java, ma un caso dove potrebbero essere relativamente costosi è come float[]contro Float[], in cui l'elaborazione di un milione di ex in sequenza potrebbe essere relativamente molto più veloce rispetto al secondo.
Dragon Energy il

1
Quando ho iniziato a pubblicare qui ho fatto lo stesso. Non essere sorpreso quando le risposte a vecchie domande tendono a ricevere poca attenzione.
JimmyJames,

1
Sono abbastanza sicuro che i tipi in scatola dovrebbero assumere più spazio dei primitivi. Ci sono potenzialmente ottimizzazioni al riguardo, ma in generale, penso che sia come descrivi. Gil Tene (della presentazione sopra) parla di una proposta per aggiungere un tipo speciale di raccolta che fornirebbe alcuni dei vantaggi delle strutture ma che utilizza ancora oggetti. È un'idea interessante, non sono sicuro dello stato del JSR. Il costo è in gran parte dovuto al tempo che è ciò a cui alludi. Se riesci a evitare che i tuoi oggetti "scappino", possono anche essere allocati in pila e non toccare mai l'heap.
JimmyJames,

0

Come è stato detto dalla gente, la creazione di oggetti non è un grosso costo in Java (ma scommetto più delle operazioni più semplici come l'aggiunta, ecc.) E non dovresti evitarlo troppo.

Detto questo, è ancora un costo e, a volte, potresti ritrovarti a cercare di scartare quanti più oggetti possibile. Ma solo dopo la profilazione ha dimostrato che questo è un problema.

Ecco una grande presentazione sull'argomento: https://www.cs.virginia.edu/kim/publicity/pldi09tutorials/memory-efficient-java-tutorial.pdf


0

Ho fatto un rapido micro-segno riguardo a questo e ho fornito tutte le fonti in github. La mia conclusione è che non è un problema se la creazione di oggetti è costosa o meno, ma la creazione continua di oggetti con l'idea che il GC si occuperà delle cose per te farà sì che l'applicazione avvii il processo GC prima. GC è un processo molto costoso ed è meglio evitarlo ogni volta che è possibile e non cercare di spingerlo per dare il via.


ciò non sembra offrire nulla di sostanziale rispetto ai punti formulati e spiegati nelle precedenti 15 risposte. Difficilmente un contenuto che valga la pena di rispondere a una domanda che è stata posta più di 2 anni fa e ha ottenuto una risposta molto approfondita
moscerino
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.