# OutOfMemory beim Clob



## JanB (2. Aug 2007)

Hallo,

da ich mich immer mehr im Kreise drehe, dachte ich mir, frage ich mal die Experten 

*Folgendes Problem:*
Aus einer Oracle-Datenbank bekomme ich einen Clob-Wert, welcher per Java in Token zerlegt werden soll und dann per Array als benutzerdefinierter Oracle-Datentyp zurückgegeben wird. Es werden 366.000 Datensätze übermittelt (kann variieren) und bei der Verarbeitung stosse ich wohl an Speichergrenzen, denn 220.000 Datensätze werden problemlos verarbeitet.
Seht ihr eine Möglichkeit diese zu umgehen (z.b. weil ich schlechte Routinen benutze, zuviel Speicher belege (System.gc() an diversen Stelle brachte leider keinen Erfolg) oder mein Ansatz ansich schon falsch ist) oder muss ich eine Speichererweiterung beantragen?

*Typen in Oracle:*
_TYPE TYPE_JAVA_RECORD IS OBEJCT (WERT1 NUMBER, WERT2 NUMBER, ... , WERT26 NUMBER);_
_TYPE TPYE_JAVA_TABLE IS TABLE OF TYPE_JAVA_RECORD;_
-> Ergo nichts anderes als ein 2d-Array.

*Clob-Ausschnitt:*

1;2923;21.27;20.27;22.82;17.85;6.80;2.46;3.72;3.49;9.99;8.62;20.78;140.81;110.36;104.73;104.33;116.72;168.98;214.95;209.01;233.92;65.01;78.43;97.32;72.54
1;2924;77.62;77.12;73.24;62.89;59.96;72.26;70.48;148.78;162.29;162.09;186.74;170.70;163.76;181.41;156.67;177.87;185.72;199.78;209.02;173.86;152.53;128.53;105.41;74.79
Dies sind 2 der ca. 360.000 Datensätze.

*Java-Quelltext:*

```
JAVA SOURCE NAMED "Java_Clob_Split" AS
import java.sql.*;
import java.io.*;
import java.util.*;    
import oracle.sql.*;
import oracle.jdbc.*;

public class Java_Clob_Split  
{ 	
    public static String returnJavaTable_String(java.sql.Clob clob_werte, oracle.sql.ARRAY[] arrayout) throws java.sql.SQLException, IOException, java.lang.VirtualMachineError
    {
        int i=0; // Datensatzzaehler

        try {
            // DB-Connection mit Oracle-Server
            DriverManager.registerDriver(new oracle.jdbc.driver.OracleDriver());
            Connection conn = DriverManager.getConnection("jdbc:default:connection"); 

            // Descriptoren für das Mapping der Oracle-Typen
            ArrayDescriptor descriptor = ArrayDescriptor.createDescriptor("TYPE_JAVA_TABLE", conn);  
            StructDescriptor struktDescriptor = StructDescriptor.createDescriptor("TYPE_JAVA_RECORD", conn);

            int i_lines = 0; // Anzahl aller Datensaetze (Lines) im Clob
            
            // Durchlauf aller Datensaetze im Clob um die gesammte Datensatzanzahl zu ermitteln (dient der Festlegung 
            // der Array Grenzen)
            if (clob_werte !=null) { 
                Reader is = clob_werte.getCharacterStream(); 
                LineNumberReader lr = new LineNumberReader(is);
                String s = lr.readLine();
                while(s!=null) {
                    i_lines++; // Zaehlen der Datensaetze
                    s=lr.readLine(); // Lesen der naechsten Zeile im Clob
                }
            }

            STRUCT[] datensaetze = new STRUCT[i_lines]; // Speichert alle Datensaetze im Format TYPE_JAVA_TABLE
            int j=0;
            Object[] werte = new Object[26]; // Enthaelt die Werte 1-26 (fuer den Type TYPE_JAVA_RECORD)

            // Auslesen des Clob-Wertes
            if (clob_werte !=null){ 
                Reader is = clob_werte.getCharacterStream(); 
                BufferedReader br = new BufferedReader(is); 


                // ##### Split-Variante 1 (Stream-Tokenizer) ##### 
                StreamTokenizer sToken = new StreamTokenizer(br);
                while (sToken.nextToken() != StreamTokenizer.TT_EOF) {
                    // Falls es sich um einen nummerischen Wert handelt -> uebernehmen und ins Array schreiben
                    if (sToken.nextToken() == StreamTokenizer.TT_NUMBER){
                        werte[j] = new Float(sToken.nval); // Wert 1-26 mit Token fuellen
                        j++ ; // nächster der 26 Werte
                        // falls Wert > 25 wieder Wert 0 des Folgendatensatzes fuellen
                        if (j > 25) {
                            // neuen Datensatz im Array anlegen (TYPE_JAVA_TABLE)
                            datensaetze[i] = new STRUCT(struktDescriptor, conn, werte);
                            j = 0;  
                            i++;
                        }
                    }
                }
            }    
                // ##### Ende Variante 1 ##############                    

            //    ############ Split-Variante 2 (String-Tolenizer #########
            //    String s = br.readLine(); 
            //    while(s!=null){ 
            //        StringTokenizer st = new StringTokenizer(s, ";"); // Trenne den String durch das Trennzeichen ;
            //         while (st.hasMoreTokens()) {
            //             werte[j] = new Float(st.nextToken()); // Wert 1-26 mit Token fuellen
            //             j++;
            //             if (j > 25) {
            //                 datensaetze[i] = new STRUCT(struktDescriptor, conn, werte);
            //                 j = 0;
            //                 i++;
            //             }
            //         } 
            //         s=br.readLine();
            //     } 
            // }
            //     #### Ende Variante 2 ######

            // Rueckgabe-Array an die PL/SQL Funktioon
            arrayout[0] = new ARRAY(descriptor, conn, datensaetze);
            return "Alles ok";
        }
        catch(OutOfMemoryError ee) { 
            return ("Fehler: "+ee + " " +i);
        }
    }	         	         
}
```

Noch ein paar Anmerkungen zum Code und den Fehlern.
Ich habe den Clob-Wert auf 2 verschiedene Arten versucht zu splitten. Beide Varianten funktionieren auch, solange weniger als ca. 220.000 Datensätze im Clob enthalten sind.

Sind es mehr bekomme ich bei 
Variante 1 die Fehlermeldung: *ORA-00932: nicht übereinstimmende Datentypen*
Variante 2 die Fehlermeldung: *java.lang.OutOfMemoryError 224217* (variiert)

Es liegt nicht an den Clob-Werten ansich (also keine Abweichungen der Sturktur ab einem bestimmten Datensatz). Es ist egal, welche 220.000 Datensätze ich auf dem Clob einlese, es treten keine Fehler auf. Nur sobald es weiter über 300.000 sind erhalte ich diese Fehlermeldungen.

Warum ich 2 unterschiedliche Fehlermeldungen erhalte (je nach Splitvariante) ist mir zwar nicht sonderlich klar, müsste aber auf die gleichen Speicherprobleme zurückzuführen sein.

Ich hoffe, mein Problem einigermaßen verständlich vermittelt zu haben und hoffe, ihr könnt mir vllt. ein paar Tipps gehen.

Vielen Dank schonmal an all diejenigen, die sich überhaupt die Mühe machen, sich den Code genauer anzuschauen!

Grüße
Jan


----------



## JanB (2. Aug 2007)

Leider habe ich mich vergessen zu registieren, somit kann ich meinen Post leider nicht mehr editieren.

Oracle Version: 9.1
Java-Version: 1.3.1


----------



## JanB (6. Aug 2007)

Ist das Problem zu speziell oder habe ich Infos vergessen, die wichtig gewesen wären?

Hoffe immer noch auf eine Antwort


----------



## SlaterB (6. Aug 2007)

Anregung: wenn du 10. Mio. (360k mal 15)  mal Werte wie 121.27 hast,
dann lohnt es sich vielleicht, die zu cachen,

erstelle ein Float-Array der Größe 10.000 mit allen Werte von 0.01 bis 999.99
und nimm Float-Objekte aus diesem Array statt ständig new Float()

---------

verwende 

Runtime runtime = Runtime.getRuntime();
        long max = runtime.maxMemory();
        long free = runtime.freeMemory();
        long total = runtime.totalMemory();

um dir generell deine Speicherbelegung anzuschauen,
z..B. alle 1000 Elemente um abzuschätzen, was du pro Datensatz brauchst

und auch zum Zeitpunkt der Exception oder kurz davor (verwendest du nur 64 MB Grundspeicher oder mehr?)


----------



## JanB (6. Aug 2007)

Vielen Dank für die Antwort!

Gleich vorne weg: 
	
	
	
	





```
long max = runtime.maxMemory()
```
 -> ist leider erst ab Java-Version 1.4 verfügbar.

Habe also etwas mit freeMemory herumexperimentiert und man konnte gut erkennen, wann der GC in Erscheinung trat. Ein expliziter GC-Aufruf, sobald freeMemory unter einen bestimmten Wert fiel, brachte leider auch nichts.

Die Idee mit den Arrays war gut, aber bei deinem Bespiel enthält es ja bereits 100.000 Elemente (nicht 10.000 ;-)) und ich habe heute erfahren, dass die Werte bis auf 3000 anwachsen können -> ergo 300.000 Elemente. 

Hatte nun trotzdem gehofft, dass jetzt der Speicher ausreicht, da 300.000 Array-Elemente (+ ~ 10 Mio Verweise) eigentlich weniger verbrauchen müssten, als ~ 10 Mio Float-Einträge. Aber leider erscheint der OutOfMemory Fehler an ähnlicher Position.



> verwendest du nur 64 MB Grundspeicher oder mehr?


Da habe ich gar keine Ahnung. FreeMemory bringt mit zum Start des Programms einen Wert von rund 4 Mio.
Ich arbeite ja direkt auf einer Oracle-Datenbank und lasse mir alle Ergebnisse als "SELECT proc_name FROM dual" zurückgeben.

Andere Ratschläge sind willkommen  :meld:


----------



## SlaterB (6. Aug 2007)

wenn dein Programm bisher keinen erhöhten Speicher benutzt,
dann kannst du ihn wahrscheinlich von derzeit 64 auf 512 MB hochsetzen
siehe google oder aktuell hier im Forum
http://www.java-forum.org/de/viewtopic.php?t=53774

falls x00.000 zuviel sind, dann müssen ja nicht alle Werte gecacht werden,
vielleicht gibt es ja eine Konzentration in einem bestimmten Bereich (z.B. unter 200 wie in deinem Beispiel)


----------

