# MySQL Abfragen blockiern den Arbeitsspeicher



## schmuck.m (18. Apr 2006)

Hallo,

ich habe das Problem das meine Abfragen auf eine MySQL auf dauer meinen ganzen Arbeitsspeicher belegen und nicht wieder freigeben.

Eine einzelne Abfrage ist nicht das Problem, nur greift sich das Programm nach der Abfrage ca 200kb des RAM und gibt diesen nicht mehr her. Dementsprechend nach relativ wenigen Abfragen ist der ganze Abeitsspeicher belegt und ich bekomme eine "java.lang.OutofMemoryError: Java heap space" Exception.

Mein Programm läuft als Servlet in einem Tomcat 5.5 Server. Die Connection zur Datenbank halte ich in einer statischen Klasse vor, welche ich mir immer mit "getInstance" hole. 


Wo hat sich hier der (logische ?!?) fehler versteckt? Ich dachte eigentlich das ich die Datenbankverbindung immer aufrecht erhalte eigentlich Speicher und Rechenzeit einspaare.



Hier mal zur Verdeutlichung meine Datenbank - Klasse:


```
package de.msis.EasyServer.Database.toolbox;

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;

import de.msis.EasyServer.ControlCenter.EasyControlCenter;
import de.msis.toolbox.XmlConfig;


public class DBConnector {

	private static DBConnector mysql = null;

	private XmlConfig config = null;
	
	private String url = null;
	private String usr = null;
	private String pwd = null;
	private String classname = "com.mysql.jdbc.Driver";
	private Connection connection = null;
	
	
	
	private DBConnector() {
		config = new XmlConfig("./conf/mysql.xml");
		url = "jdbc:mysql://" + config.getConfigValue("mysql", "host") + ":3306/" + config.getConfigValue("mysql", "libary");
		usr = config.getConfigValue("mysql", "user");
		pwd = config.getConfigValue("mysql", "passwd");
		
		try {
			Class.forName(classname);
			connection = DriverManager.getConnection(url, usr, pwd);
		} catch (SQLException e) {
			e.printStackTrace();
			EasyControlCenter.getInstance().saveLog("mysql", e);
		} catch (ClassNotFoundException e) {
			e.printStackTrace();
			EasyControlCenter.getInstance().saveLog("mysql", e);
		}
		
		
	}

	public static synchronized DBConnector getInstance() {
		if ( mysql == null ) mysql = new DBConnector();
		return mysql;
	}	
	
	public Connection getConnection() {
		return connection;
	}

	public ResultSet query(String die_frage) {

		try {
			Statement stmt = connection.createStatement();
			ResultSet rs = stmt.executeQuery(die_frage);
			return rs;
		} catch (Exception e) {
			e.printStackTrace();
			EasyControlCenter.getInstance().saveLog("mysql", e);
		}
		return null;
	}
	


	public void execute(String die_bitte) {
		try {
			Statement stmt = connection.createStatement();
			stmt.execute(die_bitte);
		} catch (Exception e) {
			e.printStackTrace();
			EasyControlCenter.getInstance().saveLog("mysql", e);
		}

	}

	public String getMaxId(String tblName, String idField) {
		ResultSet res = this.query("SELECT max(" + idField + ") AS maxid FROM " + tblName);
		try {
			while (res.next()) {
				return res.getString("maxid");
			}
		} catch (Exception e) {
			e.printStackTrace();
			EasyControlCenter.getInstance().saveLog("mysql", e);
		}

		return null;
	}

	
}
```

wenn ich nun etwas abfragen möchte arbeite ich so:


```
...
DBConnector db = DBConnector.getInstance();
ResultSet rs = db.query("select * from blablub");
...
```


----------



## DP (18. Apr 2006)

jo,

wenn du mit dem resultset fertig bist, dann muss ein

rs.close();
stmt.close();
con.close();

gesetzt werden, sonst bleibt mit dem con deine connection immer offen...


----------



## Murray (18. Apr 2006)

DP hat gesagt.:
			
		

> wenn du mit dem resultset fertig bist, dann muss ein
> 
> rs.close();
> stmt.close();
> ...



Das sollte das Speicherproblem beseitigen, allerdings sollte hier ja eigentlich die Connection absichtlich gehalten werden; durch den DBConnector-Singleton sollte sichergestellt sein, dass es nur ein Connection-Objekt gibt. Wenn man das mit close() freigibt, dann funktioniert der Code nur noch einmal.

Das ResultSet und das Statement sollten eigentlich automatisch geschlossen werden, wenn die entsprechenden Objekte collected werden - vielleicht gibt es ein memory-Leak ausserhalb des geposteten Codes?


----------



## DP (18. Apr 2006)

achso, sorry.

dann nur das stmt und das rs closen, da dies bei jedem query neu erzeugt wird.


----------



## schmuck.m (20. Apr 2006)

Hallo,

ja der Fehler war das das Statement und das ResultSet immer noch geöffnet waren. Gut, was ich immernoch nicht verstehe ist das der GC diese nicht irgendwann einfach mal aufräumt.

Ich hab das nun so gemacht das ich aus dem statischen DBConnector Objekt mir von der Connection ein Statement erzeuge und es auch wieder schließe - sind zwar ein paar zeilen mehr code pro Methode die auf die Datenbank will, aber dafür ist es sauber gemacht! 

Danke für den Tipp mit dem Closen  machmal sieht man halt vor lauter Bäumen den Wald nicht mehr 



Gruß,
Michael


----------



## DP (20. Apr 2006)

ja, der gc macht die irgendwann mal dicht, wenn der lust hat. du kannst aber auch den gc manuell anwerfen.

aber bleib mal bei der sauberen methode mit dem close


----------



## Kawa-Mike (24. Apr 2006)

Um ganz sicher zu gehen solltest du noch einen finally-Zweig einführen, wie es z.b. in der Tomcat-Dokumentation beschrieben ist.

http://tomcat.apache.org/tomcat-5.5-doc/jndi-datasource-examples-howto.html

Gerade bei langlaufenden Serveranwendungen können Dir nicht geschlossene Connections/Resultsets/Statements den RAM zumuellen und die Datenbank bleibt genau dann stehen, wenn gerade mal viel Traffic auf dem Server ist )-;

 - - - 
Connection conn = null;
  Statement stmt = null;  // Or PreparedStatement if needed
  ResultSet rs = null;
  try {
    conn = ... get connection from connection pool ...
    stmt = conn.createStatement("select ...");
    rs = stmt.executeQuery();
    ... iterate through the result set ...
    rs.close();
    rs = null;
    stmt.close();
    stmt = null;
    conn.close(); // Return to connection pool
    conn = null;  // Make sure we don't close it twice
  } catch (SQLException e) {
    ... deal with errors ...
  } finally {
    // Always make sure result sets and statements are closed,
    // and the connection is returned to the pool
    if (rs != null) {
      try { rs.close(); } catch (SQLException e) { ; }
      rs = null;
    }
    if (stmt != null) {
      try { stmt.close(); } catch (SQLException e) { ; }
      stmt = null;
    }
    if (conn != null) {
      try { conn.close(); } catch (SQLException e) { ; }
      conn = null;
    }
  }


----------

