PDA

View Full Version : [Java] Esaurire lo Heap Space


Lim
03-08-2010, 10:58
Salve a tutti,
sto sviluppando un simulatore in Java che deve tener conto del comportamento di un elevato numero di oggetti.

Con simulazioni di test, nessun problema, ma quando comincio a fare simulazioni realistiche (ho provato al momento con qualche migliaio di oggetti), esaurisco lo Heap in pochissimo tempo, incappando nell'errore:

java.lang.OutOfMemoryError: Java heap space

Come potrei risolvere il problema? Dal task manager vedo che la simulazione non eccede mai i 290MB circa di RAM...
Ho provato a modificare il file eclipse.ini ma non è cambiato nulla, con valori bassi di Xmx256m o Xmx384m, mentre per valori molto superiori non avvia Eclipse...

Esistono altri metodi per poter gestire decine di migliaia di oggetti o per aumentare lo Heap? :help:

banryu79
03-08-2010, 11:13
Come potrei risolvere il problema? Dal task manager vedo che la simulazione non eccede mai i 290MB circa di RAM...
Ho provato a modificare il file eclipse.ini ma non è cambiato nulla, con valori bassi di Xmx256m o Xmx384m, mentre per valori molto superiori non avvia Eclipse...

Non avvia Eclipse? :D

Comunque non sono i parametri per lo heap space di Eclipse che devi impostare ma i parametri dello heap space da allocare all jvm per la tua applicazione: quindi non credo tu debba toccare tanto eclipse.ini quanto i parametri da riga di comando da passare a java, quando viene lanciata la tua applicazione.

banryu79
03-08-2010, 11:16
Non avvia Eclipse? :D

Non avevo capito che era per i - valori molto alti -, pensavo fosse per i 300 e rotti MB :D
Probabilmente hai usato valori che, al momento dell'avvio di Eclipse, eccedevano la RAM disponibile sulla macchina...

Lim
03-08-2010, 11:20
Eppure ho 4GB di ram, al lancio di Eclipse ne era occupata circa il 40%...
Avevo impostato 1400, in teoria dovevano essere diponibili...

Lim
03-08-2010, 14:58
Ho effettuato anche un'altra prova e ho constatato che la memoria non viene liberata neanche invocando il garbage collector.

Spendo due parole per descrivere a grandi linee il Simulatore.
Una classe (il main) gestisce il motore dell'applicazione invocando ad ogni istante temporale un certo numero di operazioni sugli oggetti viventi.
Ogni oggetto vivente viene inserito, alla sua creazione, in una lista della classe main (ad esempio: listaDegliOggettiViventi).

Alla fine di ogni step temporale, il simulatore invoca il Garbage Collector con il comando System.gc();
Al termine di un istante temporale scelto arbitrariamente (ad es: il 100°) ho "distrutto" il 90% degli oggetti esistenti, tagliando i loro riferimenti presso il metodo main (ho svuotato la lista...).

Dal Task Manager di Windows non si rilevano rilasci di memoria. Rimane sempre occupata al massimo raggiunto da quella esecuzione. In teoria, avendo tagliato i riferimenti agli oggetti, questi non dovrebbero venire distrutti immediatamente? (Invoco io il GC e non attendo i tempi della JVM)

banryu79
03-08-2010, 15:15
Sono un ignorantone: non so dire se alla fine di una "corsa" del GC la memoria che viene "liberata" (o meglio resa disponibile) viene a sua volta rilasciata al SO dalla JVM o la JVM, già che ce l'ha sottomano, se la tiene per se (la memoria è comunque libera e a disposizione: bisognerebbe conoscere le politiche di deallocazione della JVM) e quindi tu nel task Manager non vedi apprezzabili deallocazioni.

Comunque a scanso di eqivoci ricorda che System.gc() non invoca il Garbage Collector, ma "suggerisce" solo alla JVM di lanciarlo, con tutti i "per piacere, appena puoi, eh" del caso.

@EDIT:
per monitorare le cose potresti provare jconsolle (http://download-llnw.oracle.com/javase/1.5.0/docs/tooldocs/share/jconsole.html)(è uno dei "basic tool" distribuiti con il JDK).

Lim
03-08-2010, 15:25
Eh, sono ignorante anch'io in materia :p. Quindi diciamo che non devo preoccuparmi se dal Task Manager non rilevo il rilascio della memoria.
Tanto non era quello il problema originale, volevo solo verificare che la distruzione andasse a buon fine, sperando di evitare il riempimento delle Heap.

Ora vedo cosa esce fuori con jconsolle...

banryu79
03-08-2010, 15:28
Ora vedo cosa esce fuori con jconsolle...

E' la cosa migliore.

Ma avevi letto questo passaggio, vero?

Comunque non sono i parametri per lo heap space di Eclipse che devi impostare ma i parametri dello heap space da allocare all jvm per la tua applicazione: quindi non credo tu debba toccare tanto eclipse.ini quanto i parametri da riga di comando da passare a java, quando viene lanciata la tua applicazione.

Lim
03-08-2010, 15:42
Si, l'avevo letto, ma non riesco a passarglieli. Ti spiego cosa ho fatto:

- Da console dei comandi sono entrato nella cartella contenente la classe "main".
- Ho provato a lanciare java -Xms... Xmx.... Test.class
Però mi restituisce un errore, non riesce a trovare la main class: "java.lang.NoClassDefFoundError: [...] Could not dind the main class: Test. Program will exit."

P.S. La mia classe main si chiama Test.class...

banryu79
03-08-2010, 15:48
Si, l'avevo letto, ma non riesco a passarglieli. Ti spiego cosa ho fatto:

- Da console dei comandi sono entrato nella cartella contenente la classe "main".
- Ho provato a lanciare java -Xms... Xmx.... Test.class
Però mi restituisce un errore, non riesce a trovare la main class: "java.lang.NoClassDefFoundError: [...] Could not dind the main class: Test. Program will exit."

P.S. La mia classe main si chiama Test.class...
Posizionati nella cartella che contiene il package radice della tua gerarchia e passa il nome pienamente qualificato della classe.

Comunque Eclipse, in quanto IDE, di sicuro ti permette di personalizzare i parametri per il run (run configuration) da dentro l'ide... io non son pratico, sono dell'altra sponda.

Tommo
03-08-2010, 15:48
Perchè il comando è senza .class, devi scrivere Test e basta :D

dojolab
03-08-2010, 15:49
Comunque a scanso di eqivoci ricorda che System.gc() non invoca il Garbage Collector, ma "suggerisce" solo alla JVM di lanciarlo, con tutti i "per piacere, appena puoi, eh" del caso.

Ma LOL.
Ecco perché odio Java e annessi (ogni tanto me lo ricordo) :D .

banryu79
03-08-2010, 15:53
[OT]
Ma LOL.
Ecco perché odio Java e annessi (ogni tanto me lo ricordo) :D .
Beh, è un motivo un po' debole per cassare in toto "Java e annessi".
E poi bisogna anche essere consci del perchè le cose funzionano come funzionano, prima di decidere, ma sui gusti non si discute :D

@EDIT:
[IT]
Chiaramente Tommo ha ragione. Anche quello va sistemato.

Lim
03-08-2010, 15:54
Perchè il comando è senza .class, devi scrivere Test e basta :D

Avevo provato in entrambi i modi :rolleyes:, ma non va... (anche con le maiuscole...):muro:

dojolab
03-08-2010, 16:01
[OT]

Beh, è un motivo un po' debole per cassare in toto "Java e annessi".
E poi bisogna anche essere consci del perchè le cose funzionano come funzionano, prima di decidere, ma sui gusti non si discute :D


Beh non è l unico motivo, sia chiaro :)
E' uno dei tanti :P E poi giustamente i gusti sono gusti :D

Lim
03-08-2010, 16:35
Avevo provato in entrambi i modi :rolleyes:, ma non va... (anche con le maiuscole...):muro:

Nessuna idea a riguardo? :mc:

banryu79
03-08-2010, 16:40
Nessuna idea a riguardo? :mc:
Scusa se insisto, hai letto questo (http://www.hwupgrade.it/forum/showpost.php?p=32760999&postcount=10)?

Lim
03-08-2010, 17:06
Scusa se insisto, hai letto questo (http://www.hwupgrade.it/forum/showpost.php?p=32760999&postcount=10)?

Accidenti, scusami!
Avevo letto solo i post degli altri 2 utenti ed il tuo mi si è confuso con quelli vecchi, non me ne ero proprio accorto!


Ma che intendi con "la cartella che contiene il package radice"?

Ho già fatto i seguenti tentativi:
Mi sono posizionato nella cartella \bin del package, ed ho usato il comando "java c:\workspace\prova\bin\Test",
questa prova l'ho fatta anche in \prova, senza entrare in \bin e anche da \src, ma chiamando Test.java.
Restituisce sempre lo stesso errore. Non ho mai lanciato un programma dalla console, ho sempre usato eclipse. Ora vedo se riesco ad impostare i parametri per il run direttamente da eclipse...

banryu79
03-08-2010, 17:41
Accidenti, scusami!
Avevo letto solo i post degli altri 2 utenti ed il tuo mi si è confuso con quelli vecchi, non me ne ero proprio accorto!


Ma che intendi con "la cartella che contiene il package radice"?

Ho già fatto i seguenti tentativi:
Mi sono posizionato nella cartella \bin del package, ed ho usato il comando "java c:\workspace\prova\bin\Test",
questa prova l'ho fatta anche in \prova, senza entrare in \bin e anche da \src, ma chiamando Test.java.
Restituisce sempre lo stesso errore. Non ho mai lanciato un programma dalla console, ho sempre usato eclipse. Ora vedo se riesco ad impostare i parametri per il run direttamente da eclipse...
I file .java non vanno bene di sicuro, perchè tu vuoi lanciare "java" e non "javac".

Mi spiego meglio con un esempio, mi viene più semplice.
Immagina che io nel mio pc ho creato con Eclipse un progetto "JavaCourseFondamental" (Eclipse crea su disco una cartella con questo nome) nella mia cartella "eclipseProjects" che tengo nel disco fisso "C:\".
Al progetto ho aggiunto un package "exercises" in cui a sua volta ho creato una classe di nome "Main", e l'ho compilata.

Eclipse piazza il mio bel Main.class in una sottocartella che lui(Eclipse) chiama "bin", dentro la cartella del progetto, la famosa "JavaCourseFondamental" di prima.

Ovviamente la classe Main è dotata di entry point.
Per lanciarla da consolle faccio così:
cd C:\eclipseProjects\JavaCourseFundamental\bin
java exercises.Main

Lim
03-08-2010, 18:00
I file .java non vanno bene di sicuro, perchè tu vuoi lanciare "java" e non "javac".

Mi spiego meglio con un esempio, mi viene più semplice.
Immagina che io nel mio pc ho creato con Eclipse un progetto "JavaCourseFondamental" (Eclipse crea su disco una cartella con questo nome) nella mia cartella "eclipseProjects" che tengo nel disco fisso "C:\".
Al progetto ho aggiunto un package "exercises" in cui a sua volta ho creato una classe di nome "Main", e l'ho compilata.

Eclipse piazza il mio bel Main.class in una sottocartella che lui(Eclipse) chiama "bin", dentro la cartella del progetto, la famosa "JavaCourseFondamental" di prima.

Ovviamente la classe Main è dotata di entry point.
Per lanciarla da consolle faccio così:
cd C:\eclipseProjects\JavaCourseFundamental\bin
java exercises.Main


Ti ringrazio per la pazienza e la spiegazione.
A mio avviso, l'unica differenza tra il tuo caso ed il mio è che io non ho creato il package, cioè in Eclipse le classi appartengono al Default Package, che non ha un nome vero e proprio...
Usando il tuo stesso comando non funziona (usando "default package".Main).
Sto cercando in rete se ha un altro nome o se è possibile lanciare da console il comando, ma non ho trovato ancora nulla

*andre*
03-08-2010, 18:22
guarda, in eclipse fai su:
menu run in alti -> Run configurations -> trovi il nome della classe con il main nel menu di sinistra, ci clicchi sopra e a dx appaiono i dettagli, nella scheda arguments scirvi quelli che vuoi nella sezione VM arguments :)

dopo scaricati visual vm, e lancialo prima di lanciare il programma, noterai che la tua classe con il main appare a sx ci clicchi e a dx c'è una sezione dove ci sono dei grafici, se il tuo programma lo hai fatto bene il grafico dell'heap dovrebbe essere a sega, se invece è una linea retta che punta in alto c'è qualche problema :asd:

banryu79
04-08-2010, 08:34
A mio avviso, l'unica differenza tra il tuo caso ed il mio è che io non ho creato il package, cioè in Eclipse le classi appartengono al Default Package, che non ha un nome vero e proprio...

Di solito, se non si è proprio costretti (non riesco a immaginare da cosa però) oppure se giusto-giusto non si butta giù al volo una classe di prova, sarebbe buona pratica evitare di usare il fantomatico "default package" proposto dai wizard.

A scanso di equivoci, il "default package" non esiste: significa solo che nel file sorgente della classe verrà omessa l'istruzione "package" iniziale.
Avrai così creato una classe orfana di package.

Lim
04-08-2010, 09:35
guarda, in eclipse fai su:
menu run in alti -> Run configurations -> trovi il nome della classe con il main nel menu di sinistra, ci clicchi sopra e a dx appaiono i dettagli, nella scheda arguments scirvi quelli che vuoi nella sezione VM arguments :)


Questa soluzione funziona!
negli Arguments ho messo questi due attributi: -Xms64m -Xmx1500m

L'applicazione viene lanciata correttamente, ho verificato che posso mettere qualsiasi valore purché non ecceda la memoria disponibile (come precedentemente detto da banryu79).

Ho spostato il limite della mia applicazione ma sono ancora lontano... Forse devo utilizzare un approccio diverso, ho troppi oggetti da gestire... :doh:

banryu79
04-08-2010, 10:10
Ho spostato il limite della mia applicazione ma sono ancora lontano... Forse devo utilizzare un approccio diverso, ho troppi oggetti da gestire... :doh:
Per curiosità, che requisiti hai a tal proposito? Ti è stato imposto un limite ben preciso?

Lim
04-08-2010, 10:26
Per curiosità, che requisiti hai a tal proposito? Ti è stato imposto un limite ben preciso?

Non ho un limite ben preciso, ma essendo un simulatore di un ambiente cellulare, il numero medio di oggetti da "gestire" ad ogni step temporale può essere tranquillamente dell'ordine delle decine di migliaia.

Ti faccio una breve panoramica:
Ho un certo numero di cellule (non necessariamente molte).
Ogni cellula ha migliaia di gate (anche decine di migliaia a seconda dell'ambiente da simulare).
I gate sono le porte di ingresso/uscita dei messaggi scambiati tra le cellule, i messaggi sono caricati su delle portanti. Il numero di messaggi scambiati può essere tranquillamente dell'ordine delle decine di migliaia.
Ogni oggetto modellato (cellule e portanti) viene visualizzato in 3D grazie ad una libreria a cui mi appoggio, il fatto è che questa libreria istanzia un Object3D per ogni oggetto da visualizzare (quindi raddoppio il numero di oggetti). I gate ed i messaggi non vengono visualizzati (quindi risparmio comunque decine di migliaia di oggetti).

Il sistema che ho simulato (e che esaurisce inesorabilmente lo Heap) aveva:
2 nodi da 2000 gate ciascuno (4000 oggetti software)
3000 portanti con un messaggio ciascuna (6000 oggetti software)
Ogni nodo ed ogni portante aveva un Object3D (in teoria altri 3002 oggetti software, in pratica no, perché pur essendo create tutte le portanti, queste non vengono subito rilasciate dalla cellula e quindi non vengono visualzzate. Quindi gli Object3D saranno circa un migliaio...).
Altri oggetti software vari per la gestione dell'algoritmo, qualche Observer e qualche classe per modellare gli stati (diciamo una decina circa).

TOTALE= 11000 oggetti software (circa)

*andre*
04-08-2010, 12:00
fai comunque un profiling con visualvm per vedere se il rilascio di memoria viene gestito al meglio ;)

banryu79
04-08-2010, 12:19
fai comunque un profiling con visualvm per vedere se il rilascio di memoria viene gestito al meglio ;)
Volendo, visto che lavora in Eclipse, c'è Memory Analyzer (MAT) (http://www.eclipse.org/mat/) disponibile come plugin.

Tommo
04-08-2010, 13:40
Il fatto è che fare una simulazione scientifica in Java, per di più in 3D, è contronatura :mc:

E stai iniziando a scontrarti sui motivi per cui tanta gente preferisce litigare col C++ che col "comodissimo" Java :D
Qualsiasi cosa tu stia facendo, 140 mb per 10.000 istanze è un'enormità... 14.000 byte per istanza.
Sei sicuro che la libreria 3D non sta replicando il buffer del modello per ogni Object3D? Assicurati che ce ne sia solo uno per ogni file!

Lim
04-08-2010, 14:30
Il fatto è che fare una simulazione scientifica in Java, per di più in 3D, è contronatura :mc:

E stai iniziando a scontrarti sui motivi per cui tanta gente preferisce litigare col C++ che col "comodissimo" Java :D
Qualsiasi cosa tu stia facendo, 140 mb per 10.000 istanze è un'enormità... 14.000 byte per istanza.
Sei sicuro che la libreria 3D non sta replicando il buffer del modello per ogni Object3D? Assicurati che ce ne sia solo uno per ogni file!

Purtroppo è un requisito che mi è stato imposto, avevo proposto C# perché ce l'avevo più "fresco", ma non è stato possibile venirmi incontro...

Ho verificato da poco che il problema è proprio nella libreria 3D (chissà che combina per ogni oggetto).
Non mi sembra che replichi il buffer ad ogni Object3D, ma lo verificherò in un secondo momento, per ora l'ho disattivato, perché devo verificare che il simulatore riesca a gestire almeno 50000 portanti...

P.S. sto usando JPCT come motore 3D, è il + semplice che ho trovato in giro...

banryu79
04-08-2010, 14:35
P.S. sto usando JPCT come motore 3D, è il + semplice che ho trovato in giro...
Bon, io invece presupponevo che ti appoggiassi direttamente a JOGL oppure a Java3D; JPCT non lo conoscevo.
Se pensi che stia li il problema prova a rivolgerti anche a loro direttamente (ci sarà una qualche mailing list, oppure un forum?).

Lim
04-08-2010, 14:42
Bon, io invece presupponevo che ti appoggiassi direttamente a JOGL oppure a Java3D; JPCT non lo conoscevo.
Se pensi che stia li il problema prova a rivolgerti anche a loro direttamente (ci sarà una qualche mailing list, oppure un forum?).

Avevo provato sia JOGL che Java3D, ma alla fine ha "vinto" JPCT, per la semplicità di utilizzo.
Li ho già contattati sul forum per altre ragioni, proverò a sentire che dicono...

Se a qualcuno interessa, allego il loro link (http://www.jpct.net/).