Newsgroup: it.comp.java
Titolo: problemi memoria java con windows

Gentili Utenti,
ho sviluppato un applicazione in java che fondamentalmente si occupa
di prendere dei dai dalla porta seriale ed estrapolandoli li salva in
un DB Mysql.
lo scambio dei dati avviene ogni 20 sec ed il funzionamento è h24.
Lancio l'applicazione java mediante questo comando:
java - Xms32m -Xmx512m -jar applicazione.jar
il problema è che nonostante abbia disseminato il codice con:
System.gc();
System.runFinalization();
System.gc();
la memoria occupata dall'applicazione cresce in continuazione mandando
in blocco il programma dopo qualche giorno.
nel Task manager di windows leggo utilizzo memoria java.exe 94Mb dopo
solo mezza giornata di utilizzo del programma.
Chiudendo e riavviando l'applicazione l'utilizzo della memoria scende
a 21Mb.
Da cosa può dipendere?
faccio un uso massiccio di Select sul database (circa 20 ogni 4
minuti) e dopo tento di liberare le risorse, ma con scarsi risultati
evidentemente
aspetto fiducioso suggerimenti.
Saluti
Sal

 Da cosa può dipendere?
difficile dirlo senza codice sottomano.
ad occhio non chiudi gli statement e i resultset. oppure ti salvi i dati
in una struttura che cresce cresce cresce e che non svuoti mai... le cause
possono essere molte.


la classe che uso per effettuare le select è:
<CODE>
package tk_data_3_0b;
import java.sql.*;
public class ExecuteQueryDB {
	 String Val_min;
	 String Val_max;
	 String S_max;
	 String param1;
	 String param2;
	 String param3;
	 String param4;
	 String param5;
	 String param6;
	 String param7;
	 String param8;
	 String param9;
	 String param10;
	 String param11;
	 String param12;
	 String param13;
	 String param14;
	 ResultSet resultset;
	public ExecuteQueryDB (String sql, int nparam){
	String DRIVER = "com.mysql.jdbc.Driver";
	String DB_URL = "jdbc:mysql://localhost:3306/db";
		// TODO Auto-generated method stub
//	 Carico il driver.
	try {
	  Class.forName(DRIVER);
	  // Preparo il riferimento alla connessione.
	  Connection connection = null;
	   try { // Apro la connesione verso il database.
	  connection = DriverManager.getConnection( DB_URL, "user", "pw");
		  // Ottengo lo Statement per interagire con il database.
	  Statement statement = connection.createStatement();
	  // Interrogo il DBMS mediante una query SQL.
	  resultset = statement.executeQuery(sql);
	  // Scorro e mostro i risultati.
	  while (resultset.next()) {
		  switch (nparam){
		  case 1:{
			  param1 = resultset.getString(1);
			  break;
		  }
		  case 2:{
			  param1 = resultset.getString(1);
			  param2 = resultset.getString(2);
			  break;
		  }
		  case 3:{
			  param1 = resultset.getString(1);
			  param2 = resultset.getString(2);
			  param3 = resultset.getString(3);
			  break;
		  }
		  case 4:{
			  param1 = resultset.getString(1);
			  param2 = resultset.getString(2);
			  param3 = resultset.getString(3);
			  param4 = resultset.getString(4);
			  break;
		  }
		  case 5:{
			  param1 = resultset.getString(1);
			  param2 = resultset.getString(2);
			  param3 = resultset.getString(3);
			  param4 = resultset.getString(4);
			  param5 = resultset.getString(5);
			  break;
		  }
		  case 6:{
			  param1 = resultset.getString(1);
			  param2 = resultset.getString(2);
			  param3 = resultset.getString(3);
			  param4 = resultset.getString(4);
			  param5 = resultset.getString(5);
			  param6 = resultset.getString(6);
			  break;
		  }
		  case 7:{
			  param1 = resultset.getString(1);
			  param2 = resultset.getString(2);
			  param3 = resultset.getString(3);
			  param4 = resultset.getString(4);
			  param5 = resultset.getString(5);
			  param6 = resultset.getString(6);
			  param7 = resultset.getString(7);
			  break;
		  }
		  case 8:{
			  param1 = resultset.getString(1);
			  param2 = resultset.getString(2);
			  param3 = resultset.getString(3);
			  param4 = resultset.getString(4);
			  param5 = resultset.getString(5);
			  param6 = resultset.getString(6);
			  param7 = resultset.getString(7);
			  param8 = resultset.getString(8);
			  break;
		  }
		  case 9:{
			  param1 = resultset.getString(1);
			  param2 = resultset.getString(2);
			  param3 = resultset.getString(3);
			  param4 = resultset.getString(4);
			  param5 = resultset.getString(5);
			  param6 = resultset.getString(6);
			  param7 = resultset.getString(7);
			  param8 = resultset.getString(8);
			  param9 = resultset.getString(9);
			  break;
		  }
		  case 10:{
			  param1 = resultset.getString(1);
			  param2 = resultset.getString(2);
			  param3 = resultset.getString(3);
			  param4 = resultset.getString(4);
			  param5 = resultset.getString(5);
			  param6 = resultset.getString(6);
			  param7 = resultset.getString(7);
			  param8 = resultset.getString(8);
			  param9 = resultset.getString(9);
			  param10 = resultset.getString(10);
			  break;
		  }
		  case 11:{
			  param1 = resultset.getString(1);
			  param2 = resultset.getString(2);
			  param3 = resultset.getString(3);
			  param4 = resultset.getString(4);
			  param5 = resultset.getString(5);
			  param6 = resultset.getString(6);
			  param7 = resultset.getString(7);
			  param8 = resultset.getString(8);
			  param9 = resultset.getString(9);
			  param10 = resultset.getString(10);
			  param11 = resultset.getString(11);
			  break;
		  }
		  case 12:{
			  param1 = resultset.getString(1);
			  param2 = resultset.getString(2);
			  param3 = resultset.getString(3);
			  param4 = resultset.getString(4);
			  param5 = resultset.getString(5);
			  param6 = resultset.getString(6);
			  param7 = resultset.getString(7);
			  param8 = resultset.getString(8);
			  param9 = resultset.getString(9);
			  param10 = resultset.getString(10);
			  param11 = resultset.getString(11);
			  param12 = resultset.getString(12);
			  break;
		  }
		  case 13:{
			  param1 = resultset.getString(1);
			  param2 = resultset.getString(2);
			  param3 = resultset.getString(3);
			  param4 = resultset.getString(4);
			  param5 = resultset.getString(5);
			  param6 = resultset.getString(6);
			  param7 = resultset.getString(7);
			  param8 = resultset.getString(8);
			  param9 = resultset.getString(9);
			  param10 = resultset.getString(10);
			  param11 = resultset.getString(11);
			  param12 = resultset.getString(12);
			  param13= resultset.getString(13);
			  break;
		  }
		  case 14:{
			  param1 = resultset.getString(1);
			  param2 = resultset.getString(2);
			  param3 = resultset.getString(3);
			  param4 = resultset.getString(4);
			  param5 = resultset.getString(5);
			  param6 = resultset.getString(6);
			  param7 = resultset.getString(7);
			  param8 = resultset.getString(8);
			  param9 = resultset.getString(9);
			  param10 = resultset.getString(10);
			  param11 = resultset.getString(11);
			  param12 = resultset.getString(12);
			  param13= resultset.getString(13);
			  param14 = resultset.getString(14);
			  break;
		  }
		  }
	 }
	} catch (SQLException e) {
		  // In caso di errore...
		 e.toString() ;
		 System.out.println(e);
	  } finally {
		  try{
			 // if (connection != null)
			  //System.out.println("Sto chiudendo la connessione");
				  connection.close();
		  }catch (SQLException e) {e.toString();
		  System.out.println(e);}
	    }
	}catch (ClassNotFoundException e) {
		e.toString();
		System.out.println(e);}
}
}
</CODE>
e la istanzio con la seguente istruzione:
<CODE>
ExecuteQueryDB exqimp =new ExecuteQueryDB("SELECT campo from table)",
1);
</CODE>
il secondo parametro della chiamata indica il numero di campi che la
select tira fuori (la classe è fatta per gestire fino a 14 campi)
potrebbe essere qui il prob.?
grazie
Sal

> ad occhio non chiudi gli statement e i resultset
Quoto.
Chiudi solo la connessione.
Ciao,
Paolo

grazie a tutti,
effettivamente non chiudevo gli statement ed i resultset, comunque ho
notato che nonostante le modifiche effettuate la memoria occupata ha
la tendenza a crescere.
Il problema potrebbe spostarsi nella classe che gestisce la lettura/
scrittura da seriale:
<CODE>
package tk_data_3_0c;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.*;
import java.util.Calendar;
import java.util.Enumeration;
import java.util.TooManyListenersException;
import javax.comm.CommPortIdentifier;
import javax.comm.PortInUseException;
import javax.comm.SerialPort;
import javax.comm.SerialPortEvent;
import javax.comm.SerialPortEventListener;
import javax.comm.UnsupportedCommOperationException;
import javax.swing.JOptionPane;
public class ReadAndSaveFromSerial implements  SerialPortEventListener
{
	//WRITE
	static Enumeration	      portList;
	static CommPortIdentifier portId;
	static String	      messageString = "R.D F.\r\nER.T U.\r\n";
	static SerialPort	      serialPort;
	static OutputStream       outputStream;
	static boolean	      outputBufferEmptyFlag = false;
	char dest[] = new char[100];
	int ii=0;
	//FINE WRITE
	InputStream		      inputStream;
	Thread		      readThread;
	String rb = "";
	String rb3 = "";
	String str_serial ="";
	String str_serial2 ="";
	String misura_presa ="";
	String misura_da_seriale ="";
	ReadAndSaveFromSerial() {
		boolean		      portFound = false;
		String defaultPort = "COM8";
		portList = CommPortIdentifier.getPortIdentifiers();
		while (portList.hasMoreElements()) {
			portId = (CommPortIdentifier) portList.nextElement();
			if (portId.getPortType() == CommPortIdentifier.PORT_SERIAL) {
				if (portId.getName().equals(defaultPort)) {
					// System.out.println("Found port: "+defaultPort);
					portFound = true;
					if (!portFound) {
						System.out.println("port " + defaultPort + " not found.");
					}
					// inizio scrittura
					try {
						serialPort =
							(SerialPort) portId.open("SimpleWrite", 2000);
					} catch (PortInUseException e) {
						System.out.println("Port in use.");
						continue;
					}
					try {
						outputStream = serialPort.getOutputStream();
					} catch (IOException e) {}
					try {
						serialPort.setSerialPortParams(9600,
								SerialPort.DATABITS_8,
								SerialPort.STOPBITS_1,
								SerialPort.PARITY_NONE);
					} catch (UnsupportedCommOperationException e) {}
					try {
						serialPort.notifyOnOutputEmpty(true);
					} catch (Exception e) {
						System.out.println("Error setting event notification");
						System.out.println(e.toString());
						System.exit(-1);
					}
					try {
						outputStream.write(messageString.getBytes());
					} catch (IOException e) {}
					//fine scrittura
					try {
						inputStream = serialPort.getInputStream();
					} catch (IOException e) {}
					try {
						serialPort.addEventListener(this);
					} catch (TooManyListenersException e) {}
					serialPort.notifyOnDataAvailable(true);
					try {
						Thread.sleep(2000);  // Be sure data is xferred before closing
					} catch (Exception e) {}
					serialPort.close();
				}
			}
		}
//stringa che contiene i dati provenienti dalla porta seriale
		misura_da_seriale = new String(dest);
		misura_da_seriale = misura_da_seriale.trim();
	}
	public void serialEvent(SerialPortEvent event) {
		switch (event.getEventType()) {
		case SerialPortEvent.BI:
		case SerialPortEvent.OE:
		case SerialPortEvent.FE:
		case SerialPortEvent.PE:
		case SerialPortEvent.CD:
		case SerialPortEvent.CTS:
		case SerialPortEvent.DSR:
		case SerialPortEvent.RI:
		case SerialPortEvent.OUTPUT_BUFFER_EMPTY:
			break;
		case SerialPortEvent.DATA_AVAILABLE:
			byte[] readBuffer = new byte[1];
			try {
				while (inputStream.available() > 0) {
					int numBytes = inputStream.read(readBuffer);
					rb=new String(readBuffer);
					//Array di caratteri che contiene i caratteri provenienti dalla
porta seriale
					dest[ii]=rb.charAt(0);
					ii++;
				}
			} catch (IOException e) {System.out.println("Exception Catch:"+e);}
			break;
		}
	}
}
</CODE>
oppure potrebbe essere il timer che visualizza l'orario in tempo reale
sull'interfaccia grafica?
Nelle interfacce grafiche con Swing di solito si devono chiudere degli
oggetti?
Grazie ancora
Sal

 String param14;
Usare un array no?

 la classe che uso per effettuare le select č:
Per l'amordiddio, pialla da param1 a param14 e tutto lo "switch" e al 
suo posto mettici
   String[] params;
   ...
   params = new String[nparam];
   for (int i = 0; i < params.length; i++) {
     params[i] = resultset.getString(i+1);
   }
Mi bruciano gli occhi :) ...
Prova ad usare un profiler per capire dove c'e' il memory leak, e gia' 
che ci sei leva tutti i "System.gc()" e "System.runFinalization()" che 
servono praticamente a nulla ;) !

 la classe che uso per effettuare le select è:
certo che se anche non fosse colpa di questo codice tu l'impegno ce 
l'hai messo tutto, non c'è che dire... :)
piccola postilla: ma perchè il programmatore medio java ad ogni progetto 
si deve scrivere il suo query engine su cui riesce a dare il peggio di 
se? ma che avrà mai il jdbc per scatenare cotanta fantasia? :)
voglio dare un suggerimento veloce, tante volte ti venisse voglia di un 
sano refactoring: 
http://commons.apache.org/dbutils/

 Prova ad usare un profiler per capire dove c'e' il memory leak, e gia'
da una rapida ricerca su google, ho visto che ci sono molti memory
profiler, avete qualche consiglio?
Saluti
Sal

Credo che un altro punto critico sia nella classe
ReadAndSaveFromSerial (vedi sopra)
<CODE>
while (inputStream.available() > 0) {
  int numBytes = inputStream.read(readBuffer);
  rb=new String(readBuffer);
  dest[ii]=rb.charAt(0);
  ii++;
}
misura_da_seriale = new String(dest);
misura_da_seriale = misura_da_seriale.trim();
</CODE>
Esiste un modo più efficiente per trasformare i dati provenienti dalla
seriale in una stringa?
Ancora grazie
Sal

 sano refactoring: 
http://commons.apache.org/dbutils/
Sembra promettente. Grazie(anche se non era rivolto direttamente a me)

 Sembra promettente. Grazie(anche se non era rivolto direttamente a me)
è una alternativa semplice-semplice ai framework di persistenza 
complicati, per quei progetti dove non vale la pena di introdurli e ti 
serve un query engine che faccia una semplice mappatura di bean.

Ho focalizzato dove è il problema:
l'interfaccia grafica!!
se lascio il prog acceso per 2 gg la memoria sale a 120Mb se minimizzo
ad icona il programma e poi lo ripristino l'occupazione di memoria
scende a 5Mb,
Vi risparmio il post dell'interfaccia (2000 righe) e vi chiedo se è
possibile minimizzare e ripristinare la finestra in maniera
automatica.
grazie
Saluti
Sal

Sal wrote:

Di questa cosa me ne sono accorto anche io. Solo che interfaccie di
dimensioni simili(2200-2800 righe) e classi per gestire i dati via DAO e
jdbc mi portano ad avere un consumo di circa 40 MB e minimizzando anche
soli 90 KB. Secondo me continui a gestire male il rilascio delle
risorse(non chiudi Statement, resultset...)
Il rimedio che proponi è un palliativo. Se guardi bene, appena massimizzi le
finestre, presto o tardi il consumo di memoria ricresce.


E' vero che il consumo cresce, ma minimizzando e massimizzando ogni
ora la memoria cresce ma non in modo tale da mettere in crash il
sistema.
Per quanto riguarda le eventuali chiusure, credo di aver fatto tutto
quel che si poteva fare, ho anche estrapolato anche l'interfaccia
grafica, e l'ho fatta girare , non facendo nessuna query o altre cose
al di fuori del timer che scorre a video, ed anche in questa
situazione la memoria cresce.
Adesso con 2000 linee di interfaccia grafica, non riesco proprio a
trovare il bandolo della matassa... :(
Saluti
Sal