# Out of Memory Fehler - Große Datenbankabfragen



## Schrödi (24. August 2009)

Hallo zusammen,

ich bin bin relativ neu im Bereich Java (komme aus der php Programmierung).
Ich möchte Daten aus einer Datenbank abfragen (z.Z. ca 100000 Datensätze, steigend), um diese später noch zu bearbeiten und auszugeben (wie die Ausgabe aussehen soll weiß ich bisher noch nicht, vielleicht mittels Zwischenablage, oder CSV Datei). 
Soweit bin ich ja auch noch nicht. Der Fehler tritt ja schon bei der Abfrage auf. Ich möchte die Daten in ein Array schreiben und nutze dazu folgende Funktion:


```
public String[][] query_into_array2(String query) {
        Statement st = verbinde_datenbank();


        try {
            ResultSet rs = st.executeQuery( query );

            int rows = anzahlZeilen(rs);
            int cols = anzahlSpalten(rs);

            String[][] antwort = new String[rows][cols];

            int i = 0;
            rs.first();
            while( rs.next() ) {
               for (int j = 0; j < cols; cols++){
                    antwort[i][j] = rs.getString( j );
               }
               i++;
            }

           trenne_datenbank(rs, st);

            return antwort;
        }
        catch (SQLException e) {
            String ErrorMessage = e.getMessage();
            System.out.println("SQL Fehler: " + ErrorMessage);
        }

        return null;
    }
```

Die MySQL Tabelle sollte OK sein. Laut MySQl ist sie ca. 10 Mb groß.

Wie geht man mit großen Datenmengen bei Java um? Kann man überhaupt mit  so großen Datenmengen umgehen ohne das MemoryLimit stetitg zu erweitern?

Gruß Schrodi


----------



## Franz Degenhardt (24. August 2009)

10 mb ist unproblematisch.
Bei Grössen im Bereich >1 GB sind spezielle Überlegungen nötig, da herkömmliche ApplicationServer nicht für solche Anforderungen ausgelegt sind.

Grundsätzlich sollten die gängigen Datenbankfeature voll ausgenutzt werden, also beispielsweise das Resultset dbseitig so weit wie möglich einzuschränken.

Du könntest überlegen, ob ein 2-dimensionales Array der richtige Speicherort ist. Je nachdem was mit den Daten passieren soll sind vielleicht Listen oder ähnliches sinnvoller.


----------



## Markus (24. August 2009)

Mal ein Bischen offtopic:
Das Trennen der DB Connection sollte in einem finally Block erfolgen. Im
Fehlerfall bliebe die DB Anbindung ansonsten geöffnet


----------



## zeja (24. August 2009)

Wie sieht denn deine Methode anzahlZeilen aus?


----------



## Johannes7146 (24. August 2009)

```
Two JVM options are often used to tune JVM heap size: -Xmx for maximum heap size, and -Xms for initial heap size.
```

Beispiel:
java -Xms128m -Xmx256m MeinProgramm

Diese Aufruf würde deine Application mit 128mb starten und bis maximal 256mb wachseln lassen.

EDIT: Außerdem solltest du in Schleifen die oft durchlaufen werden darauf achten das, dass nicht mehr verwendete Referenzen gelöscht werden.

Negativ Beispiel:

```
package de.tutorials.johannes7146;

import java.util.ArrayList;

public class OutOfMemory {

    public static void main(String[] args) {
        //Negativ Beispiel:
        ArrayList<Integer> liste = new ArrayList<Integer>();

        for (int i = 0; i < 2000; i++) {
            hinzufuegen(liste);
        }

    }

    private static void hinzufuegen(ArrayList<Integer> liste) {
        for (int i = 0; i < 2000; i++) {
            liste.add(new Integer(i));
        }
    }
}
```


----------



## Schrödi (24. August 2009)

Servus,
danke erstmal für so viele Reaktionen.

@ Franz Degenhardt
Die 1GB Grenze werde ich volumenmäßig wohl nicht sprengen.
Die Abfragen sollen variabel erfolgen, gesteuert durch den User. Einschränkungen durch mich sind daher nur bedingt möglich, Wenn der User alle Daten benötigt (Auswertungen über Auftragsabläufe, z.B. der Workflow eines ganzen Jahres), müssen alle Datensätze verfügbar sein.
Die Listen (oder ähnliches), die du angesprochen hast sind mir neu. Muss mich deshalb erst einlesen. Wird aber meine nächste Lektüre werden.

@ Markus
Das werde ich ändern. Habe nicht dran gedacht, dass die Verbindung noch offen ist, wenn der try Block abgebrochen wird.

@ zeja
Das ist mein ZählMethode.


```
public int anzahlZeilen(ResultSet ergebnis) {
	try {
	    int c = 0;
	    while(ergebnis.next())
		c++;
	    return c;
	}catch(SQLException sqlex) {
	    return 0;
	}
    }
```

@ Johannes7146
Ist die Speichererweiterung auch bei einem Webstart möglich? Ich arbeite mit Netbeans. Hier habe ich schon nach einer Möglichkeit gesucht, den Speicher zu erweitern; habe aber nichts gefunden. Wo diese Einstellungen zu finden sind, wird aber mit Sicherheit auch Google verraten.

Bei deinem Beispiel ist für mich leicht zu erkennen, welche Referenzen du meinst. Bei meiner Funktion sehe ich aber nicht die Lösung. (Ab und zu ist für mich ein Wink mit dem Zaunpfahl nötig , besonders zu späterer Stunde).

Gruß schrodi


----------



## zeja (24. August 2009)

Hmm ich halte das bei der von dir beschriebenen Zahl für etwas Problematisch ein Scrollable ResultSet zu verwenden. Ich weiß nicht wie das dort mit der Speicherauslastung aussieht.

Wenn du auf ein Array für deine Zeilen verzichtest und stattdessen eine ArrayList verwendest, ist das Zählen der Zeilen auch nicht mehr nötig. (Ansonsten solltest du über ein Query mit SELECT COUNT zählen).

Dazu wäre es noch interessant wie du dein Statement erstellst und zu welchem Datenbanktyp du dich verbindest.


----------



## Schrödi (24. August 2009)

Ich halte mich relativ nahe an diese Beschreibung:

https://ls10-wiki.cs.uni-dortmund.de/sopra/tutorials/jdbc

Meinen genauen Quellcode kann ich erst morgen im Büro posten.
Ich nutze MySQL Version 4 und den JDBC Treiber in der Version 5. 

Die ArrayList hört sich interessant an, das werde ich morgen mal testen., andernfalls ermittle ich die benötigten Zeilen mittels SQL. 

Gruß Schrodi


----------



## zeja (24. August 2009)

Ui und das auf ner Uni-Seite *brr*

Deutsche Methodennamen sind ja schonmal richtig pfui... und die ordentliche Behandlung mit finally-Blöcken fehlt auch in der Quelle leider gänzlich.

Was soll das ganze Programm denn letzendlich können? Baust du nur einmal eine Verbindung auf und rufst die Daten ab oder geschieht der mehrfach und gleichzeitig von verschiedenen Nutzern?

Datenbankabfragen haben leider in Java ein hohes Fehlerpotential und es kommt ein wenig darauf an was man damit vorhat, ob man nicht eventuell eine Bibliothek benutzt die das ganze vereinfacht.

Normalerweise sollte es so in der Art aussehen:


```
try {
final Connection con = createConnection();
try {
    final Statement stmt = con.createStatement();
     try {
         final ResultSet rs = stmt.executeQuery();
         try {
                // Auslesen aus dem ResultSet
         }
         finally {
              rs.close();
         }
    }
     finally {
        stmt.close();    
     }
}
finally {
   con.close();
}
}
catch(SQLException e) {
    // Behandeln der Exception oder zumindest
    // e.printStackTrace()
}
```

Wie du siehst ist der Code nicht gerade schön und lesbar. Dafür gibt es die genannten Bibliotheken wie z.B. http://www.tutorials.de/forum/java-...tenbankzugriff-mit-springs-jdbc-template.html oder commons DbUtils


----------



## Schrödi (24. August 2009)

oh. Ich dachte mit der Uni Seite hätte ich eine zuverlässige Quelle. Scheinbar doch nicht. Dein Code sieht da doch etwas logischer aus. 

Das Tool soll von mehreren Usern parallel verwendet werden. 

Von diversen Frameworks für Datenbankzugriffe habe ich auch schon gelesen, ich dachte jedoch, dass es etwas übertrieben wäre für meine wenige DBZugriffe ein Framework zu verwenden. Werde es mir jetzt aber doch mal ansehen. 

Morgen werde ich erst einmal eure Hinweise beherzigen und testen.

Gruß schrodi


----------



## Johannes7146 (25. August 2009)

Von wievielen Usern sprechen wir denn da?
Also damit meine ich nicht wieviele User es allgemein gibt, sondern wieviel maximal gleichzeitig aktiv sind.

Wenn du es so manuell programmierst hat jeder user seine eigene DB-Verbindung. (bitte korrigiert mich wenn ich falsch liege).

Es gibt die Möglichkeit das nur eine bestimmt anzahl von Verbindungen zur Verfügung stehen und die aktiven User sich diese Teilen (Connectionpool).
Wenn du weiter viel mit Java und Datenbank arbeiten wirst, schau dir doch mal hibernate an. Irgendwann wird es dir sicherlich so oder so begegnen.

Bezüglich meinem negativ Beispiel:
ich wollte damit nicht sagen, dass du etwas in der Art programmiert hast, nur das solche Konstruke oft zu einem heap Space überlauf führen.
In dem Beispiel wird auch die ArrayList verwendet. mit der Methode size() kannst auch ganz einfach die Anzahl der elemente der Liste abfragen.

EDIT: Mit Netbeans habe ich noch nicht gearbeitet. Ich entwickle mit Eclipse. Dort gibt es den Punkt "Run Configuration" dort kann man die VM- Argumente eintragen.


----------



## Schrödi (25. August 2009)

Hi,

bei diesem Tool werden es max 10 bis 20 User sein, die parallel arbeiten. Es besteht aber bereits Interesse für ein Tool, bei dem ein paar hundert User parallel arbeiten würden. 

Meine DB lässt max 100 parallele Verbindungen zu), wobei es nicht gerade performant wäre alle Kapazitäten auszunutzen.
(Seitens php hatte ich mit einem Conenctionhandling nichts zu tun. php benötigt genau eine Verbindung, gleich wieviele User das Tool nutzen  ).

Hibernate war mir auch schon ein Begriff, habe mich aber noch nicht richtig mit befasst.

Wo liegt hier der Unterschied zwischen hibernate und den von zeja genannten Springs und dbutils Klassen.
Wo liegen hier die Vor- und Nachteile?


----------



## Johannes7146 (25. August 2009)

mit dbutils habe ich bisher noch nicht gearbeitet.
Spring kann glaube ich alles was Hibernate auch kann, allerdings bringt es zusätzlich noch Möglichkeiten mit sich die Hibernate nicht kann.
Ist dadurch natürlich auch ein sehr mächtiges Framework.

Genaueres kann der aber zeja sicherlich berichten.
Ich habe bisher nur mit Hibernate gearbeitet. Es nimmt einem viel ab, da man später nur noch mit Objekten arbeitet und mit der Datenbank nichts mehr zu tun hat. (ok.... fast nicht).
Ich denke sowohl Spring als auch Hibernate brauchen eine gewisse Zeit bis man wirklich produktiv mit ihnen arbeiten kann.
Die konifguration fand ich anfangs nicht ganz leicht.
Ich muss allerdgins dazusagen, dass ich erst seit 2 Jahre programmiere.


----------



## Schrödi (26. August 2009)

Ich setze mich gerade mit Spring auseinander. 

@ Johannes7146  	
Spring beinhaltet bzw. nutzt hibernate, daher fiel meine Wahl auf Spring.

Ich folge hier einem Tutorial, das Netbeans verwendet:
http://www.javajazzup.com/issue3/page56.shtml 

Dazu muss ich die 'activate.jar' einbinden. Nach dem Einbinden bekomme ich beim Testen folgende Fehlermeldung:


```
#### Java Web Start Error:
#### JAR-Ressourcen in JNLP-Datei sind nicht von demselben Zertifikat signiert
```

Dass der Fehler auftritt, weil die activation,jar ein anderes Zertifikat hat habe ich verstanden. Aber warum ist die jetzt anders zertifiziert als die anderen Klassen und Frameworks. Ich habe die activate.jar direkt von sun geladen.

Bekomme ich irgendwo eine anders zertifiziert Datei, oder kann ich diese ändern?

Gruß schrodi


----------

