# kontinuierlicher Speicheranstieg bei DB abfrage



## Guest (19. Sep 2006)

Hallo, ich mache gerade in einer Firma Praktikum und soll zur Zeit ein Javaprogramm entwickeln, was von mehreren Datenbanken, verschiedene Tabellen und Datensätze einliest und diese in einer neuer Datenbank als Datensicherung speichert. Funktioniert alles soweit auch wunderbar wenn da nicht ein großes Problem wäre: der Speicherverbrach wächst kontinuierlich(und das Programm ist für den Dauerbetrieb konzipert und hat eine Abfrage/Aktualierungsrate von 5 sek). Habe hierfür eine extra Klasse implementiert:
Kann mir da jemand helfen, bin am verzweifeln!!
(GC arbeitet auch tadlelos, habe das mit jstat geprüft)

package datenbank;

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




/**
 * Steht für die Implementierung der Selectabfrage
 * !!!Einschränkung: es können maximal immer 10000 Datensätzegelesen werden!!!
 *
 */
public class DatenbankS implements DatenbankT {

/**speichert den Datensatz, der durch die Abfrage entstanden ist*/
    private static String[][] datensatz;

/**beinhaltet, ob der die Abfrage fehlerfrei durch geführt wurde*/
    private static boolean status;

/**die anzahl der Zeilen*/
    private static int zeilen;

/**die Anzahl der Spalten*/
    private static int spalten;

/**die ausgegebene Fehlermeldung*/
    private static String error;

/**Abfrage speichern*/
    private String query,url,user,pw,treiber;

/**Speichernung, ob offene Verbindung vorhanden*/
    private boolean isOpen;

/**Speicher die Verbungsdaten*/
    private Connection con;

/**Zeigt die Anzahl der Datensätze, die maximal auf einmal geholt werden können,
 * bei 0 wird keine Beschränkung auferlegt*/
 public final static int ANZ_DATENSAETZE = 10000;

private    ResultSet rs;

private    Statement stmt;


/**
 * Konstruktor
 *
 * baut die Verbindung auf und fragt die Daten ab.
 *
 * @param t - treiber über den JDBC die verbindung herstellt
 * @param url - entweder die URl oder die DSN bei ODBC verbindungen

 */

    public DatenbankS(Treiber t, String url) {

        this.url = t.getConnection() + url;
        treiber = t.getTreiber();
        zeilen = 0;
        spalten = 0;
        error = "";
        status = true;
        isOpen = false;

    }//Datenbankverbindung



    /**
     * baut die Verbindung auf und fragt die Daten ab.
     *
     * @param query - erwartet den Abfrage String
     * @param url - die ODBC Verbindung
     * @param user - benutzername
      * @param pw - passwort
     *
     */
    public void connect(String user, String pw){

        this.user = user;
        this.pw = pw;

            //!!!!ACHTUNG!!!! UNTERSCHEIDUNG DER ABFRAGEN NOCH VON NÖTEN -z.Zt. nur Select
            try {
                //Load the jdbc-odbc bridge driver
                Class.forName (treiber);
                //DriverManager.setLogStream(System.out);

                //Connect zum ODBC Treiber    
                con =
                    DriverManager.getConnection (url,this.user,this.pw);
                //con.setAutoCommit(false);
                isOpen = true;

                //wenn kein Connectivität, dann hier exception
                checkForWarning (con.getWarnings ());

            }//try
            catch (SQLException ex) {
                status = false;
                    System.out.println ("\n*** SQLException caught ***\n");
                    while (ex != null) {
                        System.out.println ("SQLState: " +
                        ex.getSQLState ());
                        System.out.println ("Message: " + ex.getMessage ());
                        error = "Fehler bei SELECT: "+ex.getMessage();
                        System.out.println ("Vendor: " +
                        ex.getErrorCode ());
                        ex = ex.getNextException ();
                        System.out.println ("");
                    }//while
            }//catch
            catch (java.lang.Exception ex) {
                //evtentuell andere Exceptions
                ex.printStackTrace ();
            }//catch
    }//connect

    /**
     * baut die Verbindung auf und fragt die Daten ab.
     *
     * @param query - erwartet den Abfrage String
     * @param url - die ODBC Verbindung
     * @param user - benutzername
      * @param pw - passwort
     *
     */
    public boolean abfragen(String query){

        //speichert die Query für die Count anweisung
        this.query = query;



        if(!check())return false;

            //!!!!ACHTUNG!!!! UNTERSCHEIDUNG DER ABFRAGEN NOCH VON NÖTEN -z.Zt. nur Select
            try {

                //Anzeigen welche Verbindung es ist, damit, richtige Abfarge errstellt werden kann
                stmt = con.createStatement(ResultSet.TYPE_SCROLL_SENSITIVE,ResultSet.CONCUR_UPDATABLE);

                if(!(ANZ_DATENSAETZE == 0))stmt.setMaxRows(ANZ_DATENSAETZE);
                //Schickt anfrage zur Datenbank - und speichert sie im resultset
                rs = stmt.executeQuery(this.query);
                //temp = rs.

                rs.last();
                zeilen = rs.getRow();
                rs.beforeFirst();

                int pos = 0;
                String out;
                //speichert lesbare DATEN in rsmd
                ResultSetMetaData rsmd = rs.getMetaData ();

                //anzahl der Spalten
                spalten = rsmd.getColumnCount ();

                    datensatz = new String[spalten][zeilen];
                //zeilenzeiger wieder an die erste Pos
                boolean more = rs.next();    
                //solange noch zeilen vorhanden sind, lese sie aus
                while (more) {
                    pos++;
                    for (int i=1; i<=spalten; i++) {
                        out = rs.getString(i);
                        datensatz[i-1][pos-1] = out;
                    }//for
                    more = rs.next ();
                }//while
                rs.close();


                //Close the statement
                this.stmt.close();
                System.gc();
                isOpen = true;

            }//try
            catch (SQLException ex) {
                status = false;
                    System.out.println ("\n*** SQLException caught ***\n");
                    while (ex != null) {
                        System.out.println ("SQLState: " +
                        ex.getSQLState ());
                        System.out.println ("Message: " + ex.getMessage ());
                        error = "Fehler bei SELECT: "+ex.getMessage();
                        System.out.println ("Vendor: " +
                        ex.getErrorCode ());
                        ex = ex.getNextException ();
                        System.out.println ("");
                    }//while
            }//catch
            catch (java.lang.Exception ex) {
                //evtentuell andere Exceptions
                ex.printStackTrace ();
            }//catch

        return true;
        //test();
    }//connect

    /**
     * schliesst die Verbindung
     *
     */
     public void disconnect(){

         try {
            if(!con.isClosed()){

                //Close the connection
                con.close();
                isOpen = false;//isOpen
            }//if
            }//try
            catch (SQLException ex) {
                status = false;
                    System.out.println ("\n*** SQLException caught ***\n");
                    while (ex != null) {
                        System.out.println ("SQLState: " +
                        ex.getSQLState ());
                        System.out.println ("Message: " + ex.getMessage ());
                        error = "Fehler bei INSERT: "+ex.getMessage();
                        System.out.println ("Vendor: " +
                        ex.getErrorCode ());
                        ex = ex.getNextException ();
                        System.out.println ("");
                    }//while
            }//catch
            catch (java.lang.Exception ex) {
                //evtentuell andere Exceptions
                ex.printStackTrace ();
            }//catch

     }//disconnect



    /**
     * Wenn die ersten Zeichen eine korrekte Anweisung enthalten, so wird true zurück
     * gegeben und die Art der Anweisung gespeichert!
     *
     * @return ob der Abfrage String eine Select anweisung ist
     */
    private boolean check(){

        if(query.substring(0,6).toUpperCase().equals("SELECT")){
            return true;
        }//if

        error = "Fehler bei SELECT: Die Abfrage war keine gültige Abfrabe, überprüfen Sie diese";
        status = false;
        return false;

    }//

    /**
     * Erzeugt eine Warnmeldung oder Info vom SQL Server mit Ausgabe
     * @param warn - die Warnmeldung
     * @throws SQLException
     */
    private static void checkForWarning (SQLWarning warn)throws SQLException {
        if (warn != null) {
            System.out.println ("\n *** Warning ***\n");
            while (warn != null) {
                System.out.println ("SQLState: " +
                warn.getSQLState ());
                System.out.println ("Message: " +
                warn.getMessage ());
                System.out.println ("Vendor: " +
                warn.getErrorCode ());
                System.out.println ("");
                warn = warn.getNextWarning ();
            }//while
        }//if
    }//checkForWaring


/**
 * Does ...
 *
 *
 * @return
 */
    public String[][] getDaten() {
        // your code here
        return datensatz;
    }

/**
 *
 * @return die Anuahl der Zeilen
 */
    public int getZeilen() {
        // your code here
        return zeilen;
    }

/**
 *
 * @return die Anzahl der Spalten
 */
    public int getSpalten() {
        // your code here
        return spalten;
    }

    /**
     * Zeigt an, ob die Abfarge fehlerfrei verlaufen ist
     *
     *
     * @return true- wenn die Abfrage fehlerfrei war
     */
    public boolean getStatus() {
        // your code here
        return status;
    }

    /**
     * gibt, wenn vorhanden, die SQL Fehlermeldung zurück
     * @return Fehlermeldung
     */
    public String getFehler(){
        return error;
    }//

     /**
     * gibt zurück, ob bereits eine offene Verbindung besteht
     *
     * @return true/false
     */
     public boolean isOpen(){
         return isOpen;
     }//isOpen
 }


----------



## SlaterB (19. Sep 2006)

Datenbank komplett raus, Array mit 10.000 Dummy-Inhalten füllen, evtl. bisschen verzögern falls schneller als DB,

was passiert dann im Laufe der Zeit?


----------



## SireHages (19. Sep 2006)

Was meinst du mit komplett raus??


----------



## SlaterB (19. Sep 2006)

na komplett raus im eigentlichen Sinne, auskommentieren oder wie auch immer

public boolean abfragen(String query){ 
array = dummy-Array mit 10.000 Sätzen;
return true;
}

wenn du dann immer noch das Speicherproblem hast,
dann hat das alles gar nix mit der DB zu tun 
sondern z.B. damit wo dieses Array dauerhaft im Speicher gehalten wird


----------



## SireHages (20. Sep 2006)

Also ich habe das soweit gemacht, wie du es gesagt hast!
Habe zuerst die Abfrage auskommentiert  bzw. in der methode war nur noch ein "Arraycopy" enthalten, so wie du es gesagt hattest!

Ergebnis: weiter konstanter Speicheranstieg

Danach habe ich noch zusätzlich die Methode Connect auskommentiert!

Ergebnis: anfänglichlicher Speicheranstieg(ca. 30sec), danach konstanter Wert der jeweils um 4KB schwankt - also konstant

Daraus folgere ich: Es liegt irgendwo beim Verbindungsaufbau
(korregiert mich, wenn ich was übersehen habe)

Achso: ich poste mal zum besseren Verständnis ein Stück Source Code, wie die Klasse Datenbank genutzt wird im Allgemeinen(nur struktureller Aufbau, keine sinnvolle funktionalität dahinter).

DatenbankT db;//interface

		while(true){
		db = new DatenbankS(Treiber.ODBC,URL1);
		db.connect(USER1, PW1);
		db.abfragen("SELECT ");
		db.disconnect();

		db = new DatenbankS(Treiber.ODBC,URL2);
		db.connect(USER2, PW2);
		db.abfragen("SELECT ");
		db.disconnect();
		}


----------



## Caffè Latte (20. Sep 2006)

Hi,

setzt mal für alle nicht benötigten Objekte die Referenz auf null (db =null; resultset = null; statement = null; etc.pp).


----------



## SlaterB (20. Sep 2006)

tja, das ist nicht ganz das was ich gehofft habe,
denn bei sowas wie Connection passiert das meiste im Hintergrund, da fällt mir nix zu ein,

du könntest ein bisschen rumprobieren, 
z.B. wird das Class.forName() sicherlich nur einmal pro Treiber benötigt,
die Connections selber statisch halten und wiederbenutzen?

vielleicht mal nach anderen Programmen im Netz schauen, die auch Connections verwenden,
und testen, ob dort bei Mehrfachnutzung das gleiche Problem auftritt,

ich kann da gar nix zu testen, da mir die Infrastruktur zum Ausführen fehlt


----------



## homer65 (20. Sep 2006)

So arbeitet man auch nicht. Man macht nicht für jede Abfrage eine neue Datenbankverbindung auf. Das ist zuviel Overhead. Vielmehr macht man einmal am Anfang eine Verbindung auf (connect). Dann macht man die Abfragen. Und erst am Ende, so es denn eins gibt, wird die Verbindung beendet. Um Ressourcen, die man in der Datenbank hält, freizugeben gibt es den Befehl "COMMIT", falls nicht sowieso schon "AUTOCOMMIT" eingeschaltet ist.


----------



## SireHages (20. Sep 2006)

@homer65:
das beispiel bezieht sich auf verschiedene Datenbanken(URL1,URL2)
zwischen connect und disconnect mache ich sonst verschiedene Abfragen. Ich denke mal das du das meintest.

Ich denke nun das ich den Fehler auf die schliche gekommen bin:
in der Methode disconnect, wird die Verbindung nur geschlossen, wenn sie zu ist(wie sinnlos von mir), soll heissen, es bleibt immer eine Verbindung bestehen. Problem ist aber nun, wenn ich diese schliesse bekomme folgende Exception von con.close():

[Microsoft][ODBC SQL Server Driver]Ungültiger Transaktionsstatus

-soll heissen, die Transaktion ist noch nicht abgeschlossen.
Bekomme das Problem jedoch nicht in den Griff


----------



## SlaterB (20. Sep 2006)

was ist denn mit einem Aufruf wie con.commit();
falls es sowas in der Art gibt?

noch irgendwelche Statements/ ResultsSets offen?
wiederum der Aufruf: kopiere dir erstmal ein funktionierendes JDBC-Beispiel von irgendwo her,
bevor du dich über Fehler in eigenen falsch zusammengebauten aufregst

probiere es erst mal mit einfachen Anfragen von EINEM Datensatz statt 10.000

es kann natürlich immer andere Ursachen haben,
aber wenn ein aus dem Internet kopiertes Beispiel nur mit Anpassung von Treiber/ User/ PW/ DB/ Tabellen/ Attributnamen nicht zum gleichen Fehler führt, 
dann ist wohl wieder was in deinem Programm faul


----------



## SireHages (21. Sep 2006)

also ich habe alle Statements und ResultSets geschlossen

Habe es nun so, gemacht, das ich konstant nun ingesamt 6 Verbindungen zu verschiedenen Datenbanken offen lasse. Das wollte ich eigentlich nicht, da falls den Leuten hier in der Firm irgendwann mal einfällt nen exklusiven zugriff darauf einzurichten, wird kritisch.

Commit habe ich nicht genutzt, da ich den Austocommitmode bewusst auf True hatte(ist ja eh default).

Der Speicheranstieg ist zur Zeit nur noch sehr sehr gering(langzeittest steht noch aus).Die Frage die sich mir nur noch abschliessend stellt ist, warum es durch ständiges Öffnen und Schliessen zu einem solch starken Speicheranstieg kommt. Habe eigentlich alles zum Thema disconnect beachtet!!

Danke an alle die mir versucht haben zuhelfen.


----------

