Darstellung der Liste bei vielen Daten extrem langsam

Crasher

Mitglied
Hi @ all,

mein Problem lässt sich einfach erklären, aber nicht ganz so einfach beheben. "Zumindest nicht von mir 🙂".

Ich habe eine JList, in der Daten dargestellt werden. Die Daten werden aus einem CSV-File gelesen, sortiert und anschließend in die Liste geschrieben. Da das Programm später aber mit 20.000 bis 50.000 Datensätzen arbeiten muss, habe ich mit 20.000 Datensätzen einen Test gestartet. Nun ist es aber so, dass das hHinzufügen zur Liste ziehmlich lange dauert (1-3 min). Das tritt aber nur sporadisch auf und einen genauen Weg zur Reproduktion konnte ich noch nicht feststellen.

Hier meine Methode, die meine Daten zur Liste hinzufügt:
Java:
public void showTrains(){
    this.setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
    readCSV("data/trains/collection/trainCollection.csv");
    closeBufferedReader();
    model.clear();
    sortData.clear();

    if(isEmpty == false){
      try{
        //Züge in sortData schreiben
        for(int i = 0;i<(cast.length);i++){
          sortData.add(train[i][0]);
        }

        //Die Züge sortieren
        Collections.sort(sortData, new Comparator<String>() {
            @Override
            public int compare(String o1, String o2) {
                return Integer.valueOf(o1).compareTo(Integer.valueOf(o2));
            }
        });

        //Sortierung der Züge in der Liste darstellen
        for(Iterator it = sortData.iterator(); it.hasNext();){
          model.addElement(it.next().toString());
        }

      }catch(Exception e){
        JOptionPane.showMessageDialog(this, "Fehler beim Hinzufügen der Züge zur Liste.", "Es is ein Fehler aufgetreten!", JOptionPane.ERROR_MESSAGE);
        ErrorLog errorLogging = new ErrorLog();
        errorLogging.writeErrorLog(e.toString());
      }
    }
    this.setCursor(Cursor.getDefaultCursor());
  }

Kurze Erläuterung unbekannter Faktoren:
  • Die Variable isEmpty gibt an, ob die Liste überhaupt nachgeladen werden muss. (Habe ich zur
    Einschränkung der Perfomancebelastung eingebaut)
  • model ist in dem Fall mein DefaultListModel
  • sortData meine ArrayList, die die zu sortierenden Daten enthält
  • Methode readCSV, liest die CSV aus und schreibt alle Daten in einen Array

Hoffe es kann mit jemand von euch helfen. Ich komme an der Stelle nämlich nicht mehr weiter.
Vor allem da der Fehler nur sporadisch auftritt.

Vielen Dank schonmal im Voraus.

Greetz
Crasher
 
Hm. Was dauert lange? Das Sortieren, oder das Einfügen? Es könnte schon helfen, ein neues Model zu erstellen, das dann zu füllen, und DANN erst in die Liste zu packen. Die Liste ist als Listener am Modell, und kriegt jede Änderung mit und aktualisiert sich dann...
 
Hi,

ich kann Dir leider nicht genau sagen, ob es an der Sortierung oder am Hinzufügen zur Liste liegt.
Habe es eben ausprobiert, beim Hinzufügen ein neues Model zu erstellen und dann dieses zu verwenden.
Das Problem bleibt aber.

Java:
public void showTrains(){
    this.setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
    readCSV("data/trains/collection/trainCollection.csv");
    closeBufferedReader();
    sortData.clear();

    if(isEmpty == false){
      try{
        //Züge in sortData schreiben
        for(int i = 0;i<(cast.length);i++){
          sortData.add(train[i][0]);
        }

        //Die Züge sortieren
        Collections.sort(sortData, new Comparator<String>() {
            @Override
            public int compare(String o1, String o2) {
                return Integer.valueOf(o1).compareTo(Integer.valueOf(o2));
            }
        });

        DefaultListModel newModel = new DefaultListModel();
        trainList.setModel(newModel);

        //Sortierung der Züge in der Liste darstellen
        for(Iterator it = sortData.iterator(); it.hasNext();){
          newModel.addElement(it.next().toString());
        }

      }catch(Exception e){
        JOptionPane.showMessageDialog(this, "Fehler beim Hinzufügen der Züge zur Liste.", "Es is ein Fehler aufgetreten!", JOptionPane.ERROR_MESSAGE);
        ErrorLog errorLogging = new ErrorLog();
        errorLogging.writeErrorLog(e.toString());
      }
    }
    this.setCursor(Cursor.getDefaultCursor());
  }

Danke.

Greetz
Crasher
 
Ich vermute, ich weiß woran es hängt:
ich habe mr während dem Programmablauf mal den Windows Task-Manager angeschaut und festgestellt, dass der Speicher des Programms bei jedem aufrufen der showTrains() Methode weiter steigt. Ergo nehme ich mal an, dass der Speicher nicht wieder gelöscht wird.

Beim ersten aufruf der Methode läuft nämlich noch alles Prima.
Beim zweiten Aufruf der Methode schon nicht mehr.

Jetzt stellen sich aber drei Fragen:
1. Wo haben die Daten noch Referenzen in meinem Programm?
2. Warum haben die Daten noch Referenzen in meinem Programm?
Und 3. wie räume ich den Speicherplatz wieder frei.
 
> 1. Wo haben die Daten noch Referenzen in meinem Programm?
Wo Du die Objekte noch verwendest, weißt Du am besten. 😉

> 2. Warum haben die Daten noch Referenzen in meinem Programm?
Warum Du die Objekte noch verwendest, weißt Du am besten. 😉

> Und 3. wie räume ich den Speicherplatz wieder frei.
Eigentlich gar nicht, dafür gibt es die Speicherbereinigung; und spätestens, wenn das Programm keinen weiteren Speicher anfordern kann, wird sie Speicher freigeben. Du kannst ihr mit System.gc() sagen, daß sie aufräumen soll, aber Dein Problem solltest Du anders lösen.
 
Also:
Meine Vermutung ist die von Marco13:
Mache ein ganz neues Model und packe die Sachen darein und dann weißt du der Tabelle das Model zu. Das hinzufügen zum Model braucht nicht lange dauern, aber wenn du was hinzufügtst, registriert die Tabelle das und aktualisiert sich und zeichnet sich neu. Und ich schätze, dieses Neuzeichnen und sonstige Operationen benötigen so lange.
Was du nämlich jetzt hast, ist das selbe. Du machst ein neues Model, fügst es hinzu und addest dann einzeln die Züge.
Mach aber lieber ein neues Model, füge die Züge hinzu und adde das Model der List.

Der Speicheranstieg ist hingegen in Ordnung. Es ist sogar wichtig, dass der GC jetzt nicht anspringt, weil das Laden dann NOCH länger braucht.

Der GC springt eh erst an, wenn ein Objekt keine Referenz mehr hat UND Speicher benötigt wird(hast du im Heap [isses der Heap ? kA] noch 500 MB frei, springt er nicht an).Denn die Suche nach icht referenzierten Objekten als auch das freigeben verbraucht wieder Leistung und das eine "Menge".
 
Es kann auch sein, dass der Speicher knapp wird, und er dann so mit Hin- und Herschaufeln beschäftigt ist, dass es so lange dauert (zumindest KANN die Ausführung extrem(!) langsam werden, wenn der Speicher knapp wird). Starte dein Programm mal mit
java -Xmx500m MeinProgramm
und schau ob das Problem weiterhin besteht. Ansonsten müßtes du mal einen Profiler anwerfen. Oder einen Debugger/Memory Leak Detector, FALLS du glaubst, dass irgendwo Referenzen rumliegen, die es nicht mehr geben sollte.
 
Nun ist es aber so, dass das Hinzufügen zur Liste ziehmlich lange dauert (1-3 min). Das tritt aber nur sporadisch auf...

Das Problem hatte ich vor einiger Zeit auch.

Ich habe die Sache dann genauer untersucht und festgestellt, dass dafür die offensichtlich ineffiziente Implementierung des
Java:
DefaultListModel
verantwortlich ist.

Nachdem ich eine eigene Implementierung geschrieben hatte tratt das Problem nie wieder auf und das Laufzeitverhalten wurde auch massiv gesteigert. Weshalb Sun so etwas ausliefert(e) ist mir bis heute unklar.

Zur Lösung:
Schreibe eine eigene Implementierung eines
Java:
ListModel
. Das ist übrigens sehr einfach.
 
Zuletzt bearbeitet:
Das DefaultListModel delegiert eigentlich nur an einen Vector. Wie deine "effizientere" Implementierung aussieht, würde mich jetzt schon mal interessieren 😉
 
Vom Prinzip her:

1. Nicht jedesmal ein event "feuern", wenn ein Element hinzugefügt wird. Hier hilft eine Bulk-Implementierung wie z.B setCollection(...) oder addCollection(...) und danach das event auslösen.
2. ArrayList oder LinkedList statt Vector verwenden.

Vorallem Punkt 1. ist für die beschriebenen Performanceprobleme verantwortlich.
 
Der erste Punkt ist das, was ich schon in meiner ersten Antwort erwähnt hatte. Eine spezielle Implementierung zu verwenden, die solche bulk-Operationen anbietet wäre dann nochmal eine Optimierung, man muss dann halt ein bißchen aufpassen dass man nicht Änderungen macht, über die jemand benachrichtigt werden sollte, aber nicht wird, weil die bulk-Methoden verwendet werden...
 
Der erste Punkt ist das, was ich schon in meiner ersten Antwort erwähnt hatte.

Hast recht. Hab es eben erst gelesen. Offensichtlich hat er aber deine Antwort nicht verstanden.

Eine spezielle Implementierung zu verwenden, die solche bulk-Operationen anbietet wäre dann nochmal eine Optimierung, man muss dann halt ein bißchen aufpassen dass man nicht Änderungen macht, über die jemand benachrichtigt werden sollte, aber nicht wird, weil die bulk-Methoden verwendet werden...

Ja.
 

Neue Themen


Zurück
Oben