Newsgroup: it.comp.java
Titolo: ancora su java.lang.OutOfMemoryError

Scusate se continuo a rompere, ma non ho altri amici al di fuori di voi... :-)
Ho questa applicazione di cui parlavo nei miei post precedenti che scorre un file di testo con dei dati e li inserisce in un database Oracle costruendosi via via gli statement SQL appropriati. In fase di test andava bene, ma adesso che la sto provando con file di dati "veri" (dell'ordine di decine di migliaia di record) mi da degli OutOfMemoryError da tutte le parti.
Ovviamente non posso postarvi 3000 righe di codice e chiedervi di darci un'occhiata, quindi volevo cortesemente chiedere ai piu` esperti tra voi qualche direttiva generale su come ottimizzare l'utilizzo della memoria in Java (anche un link a qualche documento in rete va benissimo).
Un dubbio che mi viene sempre e` questo: all'interno di una stessa istanza di una classe, usando le variabili di classe piuttosto che le variabili private dei metodi si  risparmia memoria? 
Cioe`, supponendo di avere un istanza della classe Test con un loop che richiama 100000 volte il metodo getString, quale dei due modi di scrivere la classe occupa meno memoria? 
public class Test () {
  public String getString () {
    String s = ... ;  // mi ricavo la stringa in qualche modo ...
    return s ;
  }
}
public class Test () {
  String s ;
  public String getString () {    
    this.s = ... ;  // mi ricavo la stringa in qualche modo ...
    return s ;
  }
}
Qualsiasi forma di aiuto sara` enormemente apprezzata... l'applicazione deve essere consegnata il 31/12 :-)
Ciao, Cris
-- 
Cristian Veronesi - CRPA SpA - Reggio Emilia, Italy
Whatever it is, I'm against it (Groucho Marx)

[...]
Al buio, :) ho l'impressione che ci siano due cose che concorrono probabilmente 
a darti i problemi: la proliferazione di oggetti, e il meccanismo di gestione 
della memoria dinamica.
1. Per il primo, a volte succede di scrivere un algoritmo che, semplicemente, 
crea troppi oggetti: ad esempio scrivere String a="a"; String b="b"; String 
c=a+b; crea tre oggetti mentre StringBuffer sb=new StringBuffer(); 
sb.append("a"); sb.append("b"); String c=sb.toString(); ne crea due. 
Quindi qui puoi agire: usa algoritmi che evitino questo fattore. BTW, nel tuo 
esempio, cosi' com'e', la risposta e' che non c'e' troppa differenza: ricorda 
che sullo stack vengono allocati i riferimenti, non gli oggetti stessi; quindi 
dichiarare una String locale ad un metodo o usare un membro come buffer *non* 
risparmia nulla se ogni volta crei una stringa nuova. 
*Ma* se usi lo stesso buffer *ammenoche' non sia indispensabile* invece la cosa 
cambia. Indispensabile e' ad esempio quando devi memorizzare la stringa in 
memoria, per usi successivi. Ma se della stringa dopo aver fatto la INSERT non 
te ne importa piu' nulla, allora puoi allocare un unico buffer invece di 10000 
- con un risparmio notevole. Non devi pero' usare String, dato che String 
incapsula il concetto di "stringa costante"
class Extractor {
 private StringBuffer sb = new StringBuffer();
 /**
  * This method always returns the same object. Therefore if the returned   
  * string  must have a life span longer than the code distance between two 
  * subsequent calls, the contents have to be saved in a separate buffer.
  */
 public StringBuffer getData() {
    sb.delete(0, sb.length()); // Pulisce il buffer
    ... riempie...
    return sb;
 }
}
e il codice chiamante sara', similmente, costruito su un altro buffer:
 private StringBuffer sb;
 private Extractor e;
 private final String INS_1= "insert into my_table (my_field) values ('"
 private final String INS_2="')";
 ...
 void insert() {
    sb.delete(0, sb.length()); // Pulisce il buffer
    sb.append(INS_1);
    StringBuffer data=getData();
    for(int i=0; i<data.length(); i++)
    	    sb.append(data.charAt(i));
    sb.append(INS_2);
    ... 
    // Ora sei costretto a generare una String
    stmt.execute(sb.toString());
 }
Qui sopra ho enfatizzato anche il riuso delle *stesse* stringhe costanti con 
INS_1 e INS_2: in realta' le ottimizzazioni del compilatore fanno la stessa 
cosa, per cui puoi lasciarle nel codice.
Altri dettagli, come ad esempio usare PreparedStatement invece di Statement, 
aiutano o meno a seconda del Driver JDBC.
2. Secondo, ricorda che il garbage collector e' un thread a bassa priorita'. 
Se, come mi par di capire, il tuo codice inizia a girare all'inizio del file e 
finisce al termine, e magari usa appunto tecniche che producono molti oggetti, 
c'e' la possibilita' che il gc non possa mai entrare in azione liberando la 
memoria. Il trucco e' semplice: invoca esplicitamente 
 System.runFinalization(); 
 System.gc() 
ad ogni passaggio ed eventualmente metti il thread in Sleep() per qualche 
millisecondo.
Monitora la memoria con Runtime.totalMemory() per vedere cosa succede. Nel caso 
il tuo algoritmo sia particolarmente oneroso in termini di creazione di 
oggetti, puoi usare java.lang.ref per indagare lo stato dei reference presenti 
e vedere cosa e come il gc fa o non fa. E quindi porvi rimedio.
Infine, c'e' la possibilita' che la tua applicazione, al meglio, richieda 
semplicemente tanta memoria. Nel qual caso, se hai una limitazione predefinita 
- devi ridisegnarla tenendo conto di questo fatto: ad esempio, prevedendo 
l'inserimento del testo in pezzi successivi con esecuzioni slegate o cose del 
genere. 
In bocca al lupo. :)
-- 
God is Real, unless declared Integer.
ObjectZone - 
http://space.tin.it/computer/csadun/



 rimedio.
Non sono riuscito a trovare questo package nella documentazione delle core API 1.1.7 - e` forse stato introdotto nella 1.2 ?
Ciao e grazie per l'aiuto, Cris


Non sono riuscito a trovare questo package nella documentazione delle core API 
1.1.7 - e` forse stato introdotto nella 1.2 ?
Temo di si' :(


Pazienza, grazie comunque per i tuoi consigli. Adesso il mio codice e` sicuramente piu` ottimizzato di prima ma il buco di memoria si e` spostato all'interno dello stesso driver JDBC (Oracle thin 7.3.4) :
java.lang.OutOfMemoryError
	at java.lang.StringBuffer.setLength(Compiled Code)
	at oracle.jdbc.driver.OracleSql.parse(Compiled Code)
	at oracle.jdbc.driver.OracleConnection.nativeSQL(Compiled Code)
	at oracle.jdbc.driver.OracleStatement.doExecuteWithTimeout(Compiled Code)
	at oracle.jdbc.driver.OracleStatement.executeUpdate(Compiled Code)
	at jmida.JMidaAbstractRecord.commit(Compiled Code)
	at jmida.JMidaSession.run(Compiled Code)
	at jmida.JMidaAgent.run(Compiled Code)
	at java.lang.Thread.run(Thread.java:466)
Qualche idea ?
Ciao, Cristian (scusate per la pedanteria)

-- 
