# System.gc() ?



## flashray (10. April 2006)

Hallo,

verwendet ihr in euren Programmen eigentlich System.gc(). Wenn ich mit meinem Editor eine Große Textdatei lade, oder in der neu implementierten Druckvorschau blättere, oder zoome kommt es vor das:
"Exception in thread "AWT-EventQueue-0" java.lang.OutOfMemoryError: Java heap space" !

Ist es zu empfehlen, wenn man mit großen Daten seis String oder Images arbeitet, System.gc() aufzurufen? Wenn ja wie und wo sollte man ihn am besten implementieren?


Vg Erdal


----------



## flashray (10. April 2006)

2. Frage zu Speicherverbrauch:

JLabel label = new JLabel();
label = null;
label = new JLabel();

Wird der GC den Speicher für das zuerst erstellte Objekt freigeben, weil keine Referenz darauf besteht, oder bleibt dieser weiterhin bestehen?

3. Frage zu Speicherverbrauch:

Was sind die häufigsten Fehler bei String und Image Operationen die den Speicher voll laufen lassen? Was wird oft vergessen?

Welche Maßnahmen kennt ihr den Speicherverbrauch zu minimieren?

Wo könnte ich einen Artikel oder ähnliches darüber lesen?


Vielen Dank!

Vg Erdal


----------



## flashray (10. April 2006)

Hallo Freunde,

Was ist mit JMX, wäre sie hierfür hilfreich:

4. Könnte man mit Hilfe von JMX ausfindig machen, welcher String, welches Image Objekt genau wieviel Speicher verbraucht, um genau die ineffiziente Stelle im Programm zu finden, um sie zu verbessern. Es würde auch reichen speicherfressende, belastende Objekte herauszufinden, um diese ressourcenschonender zu implementieren.


Vg Erdal


----------



## NomadSoul (10. April 2006)

Zu Frage 1:
In einem Profiler siehst Du wie der Speicherverbrauch sinkt. Aber in einer Realen VM ist das System.gc() nur eine Empfehlung an den GC mal den Speicher zu untersuchen. Es ist nichtgarantiert das, dass auch passiert. 
Zu 2:
Jain, es besteht zwar keine Referenz mehr auf dieses Objekt, allerdings läuft der GC nicht ständig mit. Somit kann es sein das dieses Objekt erst am Ende des Programms zerstört wird.


----------



## Thomas Darimont (10. April 2006)

Hallo!

Hallo!



> Ist es zu empfehlen, wenn man mit großen Daten seis String oder Images arbeitet, System.gc() aufzurufen? Wenn ja wie und wo sollte man ihn am besten implementieren?


Eigentlich sollte man System.gc()-Aufrufe vermeiden. Wie NomadSould schon richtig sagte, wird bei einem System.gc() Aufruf nicht notwendigerweise
ein GC ausgefuehrt. Der GC bekommt nur einen Hinweis, dass er an der naechsten Moeglichen Gelegenheit laufen soll.



> 2. Frage zu Speicherverbrauch:
> 
> JLabel label = new JLabel();
> label = null;
> ...


Ob die erste JLabel-Instanz direkt vom GC freigegeben wird haengt u.a. davon ab, wie der Heap-Speicher derzeit ausgelastet ist.
Ist dieser schon stark beansprucht, ist die Wahrscheinlichkeit hoeher, dass ein GC durchgefuehrt und die Instanz vom Heap entfernt wird.



> 3. Frage zu Speicherverbrauch:
> 
> Was sind die häufigsten Fehler bei String und Image Operationen die den Speicher voll laufen lassen? Was wird oft vergessen?
> 
> Welche Maßnahmen kennt ihr den Speicherverbrauch zu minimieren?


Die haeufigsten Fehler bei der Arbeit mit Objekten ist der, dass grosse Objekte
(grosse Strings, grosse Bilder) ueber "harte" Referenzen laenger im Speicher behalten 
werden, als eigentlich notwendig. Ein weiterer haeufiger Fehler bei Swing oder allgemein
GUI Anwendungen ist der, dass EventListener bei verschiedenen Widgets registriert,
aber spaeter nicht mehr deregistriert werden, was das aufsammeln dieser durch den GC verhindert.



> Was ist mit JMX, wäre sie hierfür hilfreich:
> 
> 4. Könnte man mit Hilfe von JMX ausfindig machen, welcher String, welches Image Objekt genau wieviel Speicher verbraucht, um genau die ineffiziente Stelle im Programm zu finden, um sie zu verbessern. Es würde auch reichen speicherfressende, belastende Objekte herauszufinden, um diese ressourcenschonender zu implementieren.


Das kannst du mit Standard JMX AFAIK nicht... dafuer musst du schon einen Profiler bemuehen der ueber das JVM TI (Tool Interface)
in den Innereinen der JVM nachschaut.

Gruss Tom


----------



## flashray (10. April 2006)

Hallo,

herzlichen Dank an NomadSoul und Tom.

Das war schon sehr Hilfreich.

Eine letzte Frage hätte ich noch: "harte" Referenzen . Wie meinst du dass?

Vg Erdal


----------



## tekilla209 (24. Mai 2007)

Genau: Was meinst Du mit "harten" Referenzen?

Ich hab da nähmlich ein ähnliches Problem. Ich lese Daten aus einer großen Textdatei zeilenweise aus. Ich weis welche Daten in welcher Reihnfolge in der Text Datei stehen und will diese dann in eine Datenbank einspeisen.
Also:

```
BufferedReader br = new BufferedReader(new FileReader(importFile));
while (line != null)
{
	line = br.readLine();
	if (line == null) break;
	String[] tupels = line.split(";");
        ...
        hier kommt dann der DBImport mit aufteilung der Daten auf mehrere Tabellen;
        ....
}
br.close();
```

Ich brauche ja eigentlich nie mehr als eine Zeile gleichzeitig im Speicher.
wenn die textdatei größer als ca. 1300 Zeilen ist dann kommt:
Exception in thread "AWT-EventQueue-0" java.lang.OutOfMemoryError: Java heap space

2. Frage: Wie kann ich die Anzahl der Zeilen aus einer Textdatei herausfinden?
-> für JProgressBar.

3. Frage: In einem anderen Prog will ich die Daten einer SQL-Tabelle in einer JTable mit JScrollPane anzeigen. Bei großen Tabellen habe ich dann auch wieder dieses Speicherproblem. Welche möglichkeit gibt es hier?


----------



## zerix (24. Mai 2007)

Hallo,

ich denke mal das Tom mit "harten" Referenzen Variablen meint, die nicht erst in der Methode erzeugt werden, in der sie gebraucht werden, und nach der Methode wieder verschwinden, sondern welche die während der gesamten Laufzeit vorhanden sind und wenn diese natürlich viel Speicher brauchen, besetzen sie den Speicher die ganze Zeit.

@tekilla
ich denke mal dass bei dir wegen dem BufferedReader der OutOfMemoryError kommt. Der puffert ja den ganzen Text aus der Datei. Das ist aber halt nur eine Vermutung.
Weißt du denn bei welcher Zeile der Error auftritt?

MFG

zEriX


----------



## Anime-Otaku (25. Mai 2007)

Zum GC...dieser wird in der Regel nur ausgeführt, wenn der Heap Speicher voll läuft und hält damit die Applikation bei vielen Daten schonmal ne Sekunde auf. Dazu kommt es beim Heap Speicher Graphen zu einem Sägezahn.

Wenn ihr das sehen wollt benutzt die JConsole, die ab Java 5 in der JDK dabei ist. (Bei 5 muss man einen Parameter zur Anwendung mitgeben um es benutzen zu können, bei 6 ist dies nicht nötig(außer bei jmx))

Wenn der Speicher trotzdem nicht ausreicht solltet ihr euren Heap Speicher (und vielleicht andere Speicherbereiche) vielleicht erhöhen, dies lässt sich jedoch nur beim Starten der Anwendung festlegen.


> Simple. For example, the java option -Xmx768m gives it a 768MByte heap, maximum but starts with just a 2MB heap. You can add -Xms256m (for example) to make the heap start at 256MB - this can be more CPU efficient if your going to need that larger space anyway, but a waste if you don't.


Dies erhöht jedoch nicht den permgen Speicherbereich, welches alle strukturellen Klassen/Methodeninfromationen bereithält.Dieser ist bei einer Anwendung normalerweise relativ konstant. Außer z.B. bei Testserver, wo man Sachen während dem Betrieb deployed/undeployed...jedoch gibt es auch einen alten jvm bug, dass beim undeployen nicht alle Daten entfernt werden. Dadurch kann es nach einigen "hot-deployments" zu einem OutOfMemory permgen kommen.


----------



## limago (25. Mai 2007)

zerix hat gesagt.:


> Hallo,
> 
> ich denke mal das Tom mit "harten" Referenzen Variablen meint, die nicht erst in der Methode erzeugt werden, in der sie gebraucht werden, und nach der Methode wieder verschwinden, sondern welche die während der gesamten Laufzeit vorhanden sind und wenn diese natürlich viel Speicher brauchen, besetzen sie den Speicher die ganze Zeit.
> 
> ...



Hi zerix,

harte Referencen sind normale Referenzen. Wenn keine harten Referenzen auf ein Object zeigen wird es gelöscht. Ein "vergessener" Zeiger führt zu einem Speicherleck. Deshalb gibt es in Java auch weiche Refrenzen.

Sieh mal hier:

http://www.gruntz.ch/papers/references.pdf

Gruss


----------



## zerix (25. Mai 2007)

Naja, im Grunde genommen hab ich das ja gemeint. ;-)

MFG

zEriX


----------



## tekilla209 (25. Mai 2007)

zerix hat gesagt.:


> @tekilla
> ich denke mal dass bei dir wegen dem BufferedReader der OutOfMemoryError kommt. Der puffert ja den ganzen Text aus der Datei. Das ist aber halt nur eine Vermutung.
> Weißt du denn bei welcher Zeile der Error auftritt?
> 
> ...



Wie kann ich readLine() ohne Bufferred Raeder implementieren?

wie bereits erwähnt kommt der fehler bei ca 1300ter Zeil (wenn der Speicher eben Voll ist).


----------



## limago (25. Mai 2007)

Das kann nicht das Problem sein. Der BufferedReader puffert zwar den Strom, aber er liest nicht dasganze File auf einmal ein. Ich habe schon Gigabyte große Dateien damit verarbeitet, ohne Probleme.

Zum Ermitteln der Zeilenzahl, wirst Du wohl die komplette Datei lesen müssen. Das geht mit Klassen aus dem java.nio Paket sehr schnell. Ich würde an Deiner Stelle schätzen. Die Zahl der Bytes kannst Du leicht ermitteln. Wenn Du ungefähr die Zahl der Bytes pro Zeile weisst, kannst Du damit die ProgressBar füttern.

Gruß


----------



## limago (25. Mai 2007)

Nachtrag. Tom hat schon angedeutet, dass Du mal einen Profiler bemühen solltest. Dann kannst Du feststellen, wer überhaupt den Speicher frisst. Mit Java wird ein Profiler ausgeliefert:

http://java.sun.com/developer/technicalArticles/Programming/HPROF.html

Das Programm sollte ordnungsgemäß beenden und wird durch den Profiler langsamer...

Gruß


----------



## tekilla209 (25. Mai 2007)

Hab mal versucht keinen BufferredReader zu verwenden und readLine()
wie folgt implementiert:

```
FileReader fr = new FileReader(importFile);
String line ="";
char c;
int i=0;
do 
{
	c = (char)fr.read();
	line += c;
	i++;
}
while (c != '\n');
```

das bringt aber nix!
ich hab immernoch mein Speicherproblem

Bitte helft mir jemand


----------



## tobias_petry (25. Mai 2007)

vllt StringBuilder verwenden?
denn wenn du an einen String etwas hinten anfügst wird ein neuer erstellt, wenn du so kurz nacheinander dutzende von neuen Strings erstellst, haut sich der Speicher natürlich voll.


----------



## limago (25. Mai 2007)

Vielleicht ist Dein Problem an einer ganz anderen Stelle. Schmeiss doch mal alles aus Deinem Programm raus, ausser dem Buffered Reader und bau langsam alles ein bis der Fehler wieder auftritt. Wenn Du den Fehler schon beim Reader hast, poste doch mal den vollständigen Code, sonst kann man Dir schwer helfen.

Ist das Lesen denn Dein Problem, oder das Befüllen der Daten für die JTable? Befüllst Du die Table aus dem File, oder der DB? Da kannst Du die Daten nartürlich ganz im Speicher halten. Da musst Du eine Klassen schreiben, welche die Daten puffert und lazy bei Bedarf nachläd. Aus Performancegründen würde ich immer Blöcke laden. 

Wenn Du die Table aus dem File füllst, würde ich Dir wieder die Pakete aus java.nio ans Herz legen. Die sind schnell und haben Puffer mit denen man gut Arbeiten kann. Wenn Du sie aus der DB füllst, gibst es im Netz eine Menge geigneter Algorithmen.

Gruß


----------



## limago (25. Mai 2007)

tobias_petry hat gesagt.:


> vllt StringBuilder verwenden?
> denn wenn du an einen String etwas hinten anfügst wird ein neuer erstellt, wenn du so kurz nacheinander dutzende von neuen Strings erstellst, haut sich der Speicher natürlich voll.




In der Tat, habe ich übersehen. Nimm doch Stringbuffer, bzw. Stringbuilder dafür. Ist auch schneller...  Dennoch, ein lauffähiges Codebeispiel zum nachvollziehen des Fehler wäre nett.


----------



## Andron (25. Mai 2007)

Keine Ahnung, ob das geht .
Ansatz:


<äußere Schleife>
Man liest in einer Schleife Zeichen aus der Datei, bis das Zeichen nicht "\n" ist, sprich end of line, dabei werden gelesene Zeichen gepuffert.
Wenn das Zeichen "\n" ist, wird ein Eintrag für die DB erzeugt.
Es wird die Zeichenstelle gemerkt und beim nächsten Durchgang wird ab der Stelle+1 <-- das wäre dann die nächste Zeile, weitergelesen.
</äußere Schlefe>

Wenn es keine Zeichen mehr gibt, wird die äußere Schleife verlassen.

Man könnte das ausprobieren oder irgendie in der Richtung. Dabei wird nur die aktuelle Zeile im Speicher gehalten.

Oder irre ich mich?


----------



## limago (25. Mai 2007)

Dieser Code erzeugt ohne Probleme ein Gigabyte großes File und liest es wieder aus. Dabei gibt es keine Speicherprobleme. Also worüber reden wir hier?


```
import java.io.BufferedReader;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.InputStreamReader;
import java.io.PrintWriter;


public class CreateBigFile {

	/**
	 * @param args
	 * @throws Exception 
	 */
	public static void main(String[] args) throws Exception {
		
		String zeile = "0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789";
		FileOutputStream outStream = new FileOutputStream("bigfile.txt");
		PrintWriter out = new PrintWriter(outStream);
		for (int i = 0; i < 10000000; i ++)
			out.println(zeile);
		
		out.close();
		int i = 0;
		BufferedReader reader = new BufferedReader(new InputStreamReader(new FileInputStream("bigfile.txt")));;
		while ( (zeile = reader.readLine()) != null){
			if ((i++ % 100000) == 0 )
				System.out.println("Lesepunkt" + i);
		}
		
		reader.close();
	}

}
```


----------



## tekilla209 (29. Mai 2007)

Vielen Dank für all eure Antworten!

ich Troll hatte den Fehler doch beim Import und der war sogar ganz offensichtlich  schäm

jetzt beschäftigt mich nur noch Frage 3 für ein anderes Project.



> In einem anderen Prog will ich die Daten einer SQL-Tabelle in einer JTable mit JScrollPane anzeigen. Bei großen Tabellen habe ich dann auch wieder dieses Speicherproblem. Welche möglichkeit gibt es hier?



ich poste jetzt an dieser Stelle keinen code, weil ganz offensichtlich ist, dass ich keine 100000 Datensätze in eine JTable speichern kann ohne Heap-Overflow. Die Frage ist nur welche alternativen hab ich.
Am liebsten wäre mir eine lösung, die keine scrollbaren ResultSets verwendet, weil mein Prog mit verschiedenen DBMSn arbeiten soll, auch mit solchen, die keine scrollbaren ResultSets unterstützen.

Danke für eure Ideen!


----------



## limago (29. Mai 2007)

Unter JTable und Paging wirst Du bei Google fündig:

ein Beispiel als Basis für eine eigene Implementierung..

Quelle: http://examples.oreilly.com/jswing2/code/ch16/PagingModel.java


```
// PagingModel.java
// A larger table model that performs "paging" of its data.  This model
// reports a small number of rows (like 100 or so) as a "page" of data.  You
// can switch pages to view all of the rows as needed using the pageDown()
// and pageUp() methods.  Presumably, access to the other pages of data is
// dictated by other GUI elements such as up/down buttons, or maybe a text
// field that allows you to enter the page number you want to display.
//
import javax.swing.table.*;
import javax.swing.*;
import java.awt.event.*;
import java.awt.*;

public class PagingModel extends AbstractTableModel {

  protected int pageSize;
  protected int pageOffset;
  protected Record[] data;

  public PagingModel() {
    this(10000, 100);
  }

  public PagingModel(int numRows, int size) {
    data = new Record[numRows];
    pageSize = size;
    
    // Fill our table with random data (from the Record() constructor).
    for (int i=0; i < data.length; i++) {
      data[i] = new Record();
    }
  }

  // Return values appropriate for the visible table part.
  public int getRowCount() { return Math.min(pageSize, data.length); }
  public int getColumnCount() { return Record.getColumnCount(); }

  // Work only on the visible part of the table.
  public Object getValueAt(int row, int col) {
    int realRow = row + (pageOffset * pageSize);
    return data[realRow].getValueAt(col);
  }

  public String getColumnName(int col) {
    return Record.getColumnName(col);
  }

  // Use this method to figure out which page you are on.
  public int getPageOffset() { return pageOffset; }

  public int getPageCount() { 
    return (int)Math.ceil((double)data.length / pageSize);
  }

  // Use this method if you want to know how big the real table is . . . we
  // could also write "getRealValueAt()" if needed.
  public int getRealRowCount() {
    return data.length;
  }

  public int getPageSize() { return pageSize; }
  public void setPageSize(int s) { 
    if (s == pageSize) { return; }
    int oldPageSize = pageSize;
    pageSize = s;
    pageOffset = (oldPageSize * pageOffset) / pageSize;
    fireTableDataChanged();
/*
    if (pageSize < oldPageSize) {
      fireTableRowsDeleted(pageSize, oldPageSize - 1);
    }
    else {
      fireTableRowsInserted(oldPageSize, pageSize - 1);
    }
*/
  }

  // Update the page offset and fire a data changed (all rows).
  public void pageDown() {
    if (pageOffset < getPageCount() - 1) {
      pageOffset++;
      fireTableDataChanged();
    }
  }

  // Update the page offset and fire a data changed (all rows).
  public void pageUp() {
    if (pageOffset > 0) {
      pageOffset--;
      fireTableDataChanged();
    }
  }

  // We provide our own version of a scrollpane that includes
  // the page up and page down buttons by default.
  public static JScrollPane createPagingScrollPaneForTable(JTable jt) {
    JScrollPane jsp = new JScrollPane(jt);
    TableModel tmodel = jt.getModel();

    // Don't choke if this is called on a regular table . . .
    if (! (tmodel instanceof PagingModel)) {
      return jsp;
    }

    // Okay, go ahead and build the real scrollpane
    final PagingModel model = (PagingModel)tmodel;
    final JButton upButton = new JButton(new ArrowIcon(ArrowIcon.UP));
    upButton.setEnabled(false);  // starts off at 0, so can't go up
    final JButton downButton = new JButton(new ArrowIcon(ArrowIcon.DOWN));
    if (model.getPageCount() <= 1) {
      downButton.setEnabled(false);  // One page...can't scroll down
    }

    upButton.addActionListener(new ActionListener() {
      public void actionPerformed(ActionEvent ae) {
        model.pageUp();

        // If we hit the top of the data, disable the up button.
        if (model.getPageOffset() == 0) {
          upButton.setEnabled(false);
        }
        downButton.setEnabled(true);
      }
    } );

    downButton.addActionListener(new ActionListener() {
      public void actionPerformed(ActionEvent ae) {
        model.pageDown();

        // If we hit the bottom of the data, disable the down button.
        if (model.getPageOffset() == (model.getPageCount() - 1)) {
          downButton.setEnabled(false);
        }
        upButton.setEnabled(true);
      }
    } );

    // Turn on the scrollbars; otherwise we won't get our corners.
    jsp.setVerticalScrollBarPolicy
        (ScrollPaneConstants.VERTICAL_SCROLLBAR_ALWAYS);
    jsp.setHorizontalScrollBarPolicy
        (ScrollPaneConstants.HORIZONTAL_SCROLLBAR_ALWAYS);

    // Add in the corners (page up/down).
    jsp.setCorner(ScrollPaneConstants.UPPER_RIGHT_CORNER, upButton);
    jsp.setCorner(ScrollPaneConstants.LOWER_RIGHT_CORNER, downButton);

    return jsp;
  }
}
```


----------



## limago (30. Mai 2007)

Noch ein Nachtrag. Wenn Du Deinen Selects eine Zeilennummer mit gibst, kannst Du nach einem Seitenwechsel per SQL die Daten leicht nachladen. Dann spielt es keine Rolle, ob Dein ResultSet scrollbar ist. 

Beispiel für einen solchen select:


```
SELECT emp_id, lname, fname, job_id, 
(SELECT COUNT(*) FROM employee e2 WHERE e2.lname <= e.lname AND e2.job_id = 10) AS rownumber
FROM employee e
WHERE job_id = 10
ORDER BY lname
```


----------



## tekilla209 (6. Juni 2007)

@limago deine Lösung sieht ja nicht schlecht aus. Allerdings benutzt Du parkete von J2EE und ich nutze J2SE. Kann ich die benötigten API für Record irgendiw einzeln laden und in meinem prog verwenden? Oder welche Lösung kannst Du mir sonst empfehlen?


----------



## limago (7. Juni 2007)

Hi,

Es sind keine Pakete von J2EE dabei. Eine Record-Klasse kannst Du leicht selbst schreiben. Sie kapselt den Zugriff auf die Datenbank. 

Ich würde stattdessen eher eine Tableklasse schreiben, mit den einzelnen Methoden

int getColumnCount();
String getValue(int column);
String getValue(String columnName);
String getColumnName(int column);
long getRowCount();
boolean moveToRow(long row);

oder meinetwegen auch

String valueAt(long row, int column);

Den SelectString kann man schon bei der Konstruktion mitgeben. Das Paging-Model bedient sich dann aus dieser Klasse ( die am besten Ihrerseits die Zeilen puffert). Die Klasse "ArrowIcon" ist nur zum malen auf die Buttons da.

Habe leider etwas Stress, sonst würde ich mal eine Lösung schreiben. Vielleicht nächste Woche.

Gruß


----------



## tobias_petry (7. Juni 2007)

ich hätte auch mal eine Frage zu System.gc()
Diese Methode weist ja den Garbage Collector an sobald wie möglich zu laufen, gibt es denn eine Möglichkeit rauszufinden wann er läuft oder was ich eigentlich brauche herauszufinden wann er fertig ist mit seinem Durchlauf ?


----------



## limago (7. Juni 2007)

Ich verstehe die Frage nicht ganz.

Wenn Du wissen willst, wann der GC läuft kannst Du die VM mit entsprechenden Parametern starten. Dann werden die Läufe in der Konsole protokolliert. 

Siehe hier: http://java.sun.com/developer/JDCTechTips/2004/tt0420.html#2
und hier :http://java.sun.com/developer/JDCTechTips/2004/tt0122.html#2

Wenn Du aus Deiner Anwendung heraus wissen willst, ob ein Objekt definitiv gelöscht wurde, schaue in diesen Thread

http://www.tutorials.de/forum/java/273920-call-value-daten-vom-heap-entfernen.html

Oder willst Du nur ganz schnöde wissen, ob er überhaupt gelaufen ist? Dann wäre ein GC-Monitor nicht schlecht. Hier eine Lösung für Java 6:

http://www.java-forum.org/de/viewtopic.php?p=271063

Gruss


----------



## Thomas Darimont (7. Juni 2007)

Hallo,

eine andere Varainte um festzustellen, ob ein GC erfolgte oder nicht wäre es IMHO die  benutzen Memory Bereiche zu tracken.

Hmmm also ich denke diese Form des Memory Trackers ist schon ziemlich grob...

```
public class GCMonitor implements Runnable {
		Map<GarbageCollectorMXBean, Long> garabageCollectorCollectionCountMap = new WeakHashMap<GarbageCollectorMXBean, Long>();

		@Override
		public void run() {

			initializeGarbageCollectorCounters();

			while (true) {
				for (GarbageCollectorMXBean garbageCollectorMXBean : ManagementFactory
						.getGarbageCollectorMXBeans()) {

					long currentGarbageCollectorCollectionCount = garbageCollectorMXBean
							.getCollectionCount();

					if (currentGarbageCollectorCollectionCount > garabageCollectorCollectionCountMap
							.get(garbageCollectorMXBean)) {
						garabageCollectorCollectionCountMap.put(
								garbageCollectorMXBean,
								currentGarbageCollectorCollectionCount);
						System.out.println("GC was performed by: "
								+ garbageCollectorMXBean.getName());
					}
				}

				try {
					TimeUnit.SECONDS.sleep(1L);
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
			}
		}

		private void initializeGarbageCollectorCounters() {
			for (GarbageCollectorMXBean garbageCollectorMXBean : ManagementFactory
					.getGarbageCollectorMXBeans()) {
				garabageCollectorCollectionCountMap.put(garbageCollectorMXBean,
						garbageCollectorMXBean.getCollectionCount());
			}
		}
	}
```
Leider implementiert das GarbageCollectorMXBean  kein NotificationBroadcaster Interface so das man hier keine NotificationListener anhängen kann. Na ja, vielleicht gibts ja noch einen versteckten JVM-Hook worüber man direkt über GC Events informiert wird. Andere JVM's wie etwa BEA's JRockit bieten sowas IMHO über eine proprietäre API an.

Anonsten gefällt mir die Variante mit der Reference Queue doch besser... da wird man wirklich "direkt" nach dem GC Informiert, da die remove Methode an der ReferenceQueue so lange blockiert bis die nächste (Weak\Soft\Phantom) Reference verfügbar ist. (Und das ist sie eben unmittelbar nach dem finalen GC des gewrappedten Objekts)

Gruß Tom


----------



## tekilla209 (11. Juni 2007)

Danke nochmal für eure Antworten, spezziell @limago

habs jez hinbekommen ;-)


----------



## tekilla209 (15. Juni 2007)

limago hat gesagt.:


> Hi,
> 
> Es sind keine Pakete von J2EE dabei. Eine Record-Klasse kannst Du leicht selbst schreiben. Sie kapselt den Zugriff auf die Datenbank.
> 
> ...



Danke nochmal für deine Mühe. Bei meiner Lösung habe ich die Tableklasse so implementiert, dass Sie alle Daten in ein Array reinschreibt. Auf diese Art und Weise, kann ich mir zwar sehr viel größere Tabellen anzeigen lassen, als innerhalb einer einzigen ScrollPane aber bei wirklich großen Tabellen hab ich natürlich immernoch das Speicherproblem. Meine Frage ist wie kann ich die Methoden boolean moveToRow(long row); und String valueAt(long row, int column); ohne scrollable Resultsets implementieren?


----------



## tekilla209 (15. Juni 2007)

limago hat gesagt.:


> Hi,
> 
> Es sind keine Pakete von J2EE dabei. Eine Record-Klasse kannst Du leicht selbst schreiben. Sie kapselt den Zugriff auf die Datenbank.
> 
> ...



Danke nochmal für deine Mühe. Bei meiner Lösung habe ich die Tableklasse so implementiert, dass Sie alle Daten in ein Array reinschreibt. Auf diese Art und Weise, kann ich mir zwar sehr viel größere Tabellen anzeigen lassen, als innerhalb einer einzigen ScrollPane aber bei wirklich großen Tabellen hab ich natürlich immernoch das Speicherproblem. Meine Frage ist wie kann ich die Methoden boolean moveToRow(long row); und String valueAt(long row, int column); ohne scrollable Resultsets implementieren?


----------



## limago (30. August 2007)

Aus aktuellem Anlass (Nachfrage) führe ich diesen Thread weiter...

Zum Problem:

Ich hatte Dir ein SQL-Beispiel gepostet, mit dem Du die Daten "päckchenweise" aus der Datenbank auslesen kannst. Wenn Du gemäß meines  Beispiels im SQL-Statement Zeilennummern erzeugst, kannst Du mit einer einfachen Abfrage jeweils so viele Datensätze lesen, wie Du verarbeiten kannst.

Hier nochmal der SQLString:

SELECT emp_id, lname, fname, job_id, 
(SELECT COUNT(*) FROM employee e2 WHERE e2.lname <= e.lname AND e2.job_id = 10) AS rownumber
FROM employee e
WHERE job_id = 10
ORDER BY lname


Der Unterselect "(SELECT COUNT(*) FROM employee e2 WHERE e2.lname <= e.lname AND e2.job_id = 10) AS rownumber" erzeugt eine Zeilennummer. 

Mache daraus einen View (Abfrage), dann kannst du einen Select auf den view Ausführen, hast aber immer zeilennummern in der Abfrage.

Dadurch kannst du mit
Select * from MyView where rownumber between x and y

die Daten paketweise lesen. Also nicht alle Daten auf einmal in ein Array packen, sondern das TableModel (oder wer auch immer) lädt bei Bedarf immer nur wenige hundert Zeilen über diesen View ein. damit können Deine Tabellen Terrabyte groß sein, Du liest nur was Du brauchst...

Gruß


----------



## tekilla209 (3. September 2007)

Vielen Dank für Deine Antwort!

Mein Problem ist leider, dass der User meines ies die DB zur Laufzeit auswählt und auch SQL-Statements zur Laufzeit eingeben können soll. Von diesen Statements brauche ich denn das Ergebnis innerhalb meiner JTable, deshalb kann ich keine Views anlegen, weil ich eben vorher nicht weis wie die entsprechende DB aussieht.
Ich könnte höchstens versuchen die Strings mit den SQL-Statements auseinander zu nehmen und automatisch ein Feld mit der Zeilennummer in diese Statements einzufügen, aber allein schon bei dem Gedanken daran wird mir schlecht


----------



## limago (3. September 2007)

Hi,

wenn ich mal mehr Zeit habe, werde ich dem dem Problem mal näher widmen. Aaaaber andere Leute haben das Problem ja auch. In der Regel wird es dadurch gelöst, dass man erst einen Select count(*) auf die where -Klausel los läßt, dann beim Überschreiten einer Schwelle den Anwender zur Präzessierung der Auswahl auffordert. 

Ist es möglich mehr über die Hintergründe zu erfahren? Möglicherweise, kann man das Problem ja an einer ganz anderen Stelle lösen.

Meine Neugier ist geweckt.

Grüße


----------



## tekilla209 (14. September 2007)

Also... ich hab mich dann mal entschlossen für verschiedene DBMSs verschiedene PagingModels zu schreiben.

hier einmal das zu MSSQL geht auch mit Oracle:

```
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.sql.SQLException;
import java.sql.Statement;

import javax.swing.table.*;
import javax.swing.*;
import java.awt.event.*;

public class MSSQLPagingModel extends AbstractTableModel
{
	private Connection conn;
	private ResultSet rs;
	private ResultSetMetaData rsmd;
	private String statement;
	
	protected int pageSize;
	protected int pageOffset;
	private int row;
	private int rowCount;
	private int columnCount;
	private String[] columnNames;
	protected String[][] data;

	public MSSQLPagingModel(String statement)
	{
		this.statement = statement;
		conn = DBConnection.getConnection();
		row = 0;
		pageOffset = 0;
		pageSize = 100;
		try 
		{
			rs = DBConnection.executeQuery(statement);
			rsmd = rs.getMetaData();
			//Zeilenzahl berechnen
			rs.last();
			rowCount = rs.getRow();
			rs.beforeFirst();
			//////
			columnCount = rsmd.getColumnCount();
			columnNames = new String[columnCount];
			for (int i=0; i<columnCount; i++)
			{
				columnNames[i] = rsmd.getColumnName(i+1);
			}
			data = new String[pageSize][columnCount];
			int i=0;
			while (rs.next() && i<pageSize)
			{
				for (int j=0; j<columnCount; j++)
				{
					data[i][j]= rs.getString(j+1);					
				}
				i++;
			}
			rs.close();
		}
		catch (SQLException e) 
		{
			e.printStackTrace();
		}
	}

	//Return values appropriate for the visible table part.
	public int getRowCount() { return Math.min(pageSize, rowCount); }
	public int getColumnCount() { return columnCount; }

	// Work only on the visible part of the table.
	public Object getValueAt(int row, int col) 
	{
	 return data[row][col];
	}
	public String getColumnName(int col) 
	{
	 return columnNames[col];
	}
	//Use this method to figure out which page you are on.
	public int getPageOffset() { return pageOffset; }

	public int getPageCount() 
	{ 
	 return (int)Math.ceil((double)rowCount / pageSize);
	}

	// Use this method if you want to know how big the real table is . . . we
	// could also write "getRealValueAt()" if needed.
	public int getRealRowCount() 
	{
	 return rowCount;
	}

	public int getPageSize() { return pageSize; }
	
	public void setPageSize(int s) 
	{ 
	 if (s == pageSize) { return; }
	 int oldPageSize = pageSize;
	 pageSize = s;
	 pageOffset = (oldPageSize * pageOffset) / pageSize;
	 fireTableDataChanged();
	 if (pageSize < oldPageSize) 
	 {
	   fireTableRowsDeleted(pageSize, oldPageSize - 1);
	 }
	 else 
	 {
	   fireTableRowsInserted(oldPageSize, pageSize - 1);
	 }
	}

	//Update the page offset and fire a data changed (all rows).
	public void pageDown() 
	{
	 if (pageOffset < getPageCount() - 1) 
	 {
	   pageOffset++;
	   //System.out.println(pageOffset);
	   try 
	   {
		   rs = DBConnection.executeQuery(statement);
		   rs.first();
		   rs.relative((pageOffset*pageSize)-1);
		   int i=0;
		   while (rs.next() && i<pageSize)
		   {
			   for (int j=0; j<columnCount; j++)
			   {
				   data[i][j] = rs.getString(j+1);
			   }
			   i++;
		   }
		   //letzte Seite mit leerzeichen füllen
		   for (int k=i; k<pageSize; k++)
		   {
			   for (int j=0; j<columnCount; j++)
			   {
				   data[k][j] = "";
			   }
		   }
	   }
	   catch (SQLException e)
	   {
		   e.printStackTrace();
	   }
	   fireTableDataChanged();
	 }
	}

//	 Update the page offset and fire a data changed (all rows).
	public void pageUp() 
	{
	 if (pageOffset > 0) 
	 {
	   pageOffset--;
	   //System.out.println(pageOffset);
	   try 
	   {
		   rs = DBConnection.executeQuery(statement);
		   rs.first();
		   rs.relative((pageOffset*pageSize)-1);
		   //System.out.println(test);
		   int i=0;
		   while (rs.next() && i<pageSize)
		   {
			   for (int j=0; j<columnCount; j++)
			   {
				   data[i][j] = rs.getString(j+1);
			   }
			   i++;
		   }
	   }
	   catch (SQLException e)
	   {
		   e.printStackTrace();
	   }
	   fireTableDataChanged();
	 }
	}

//	 We provide our own version of a scrollpane that includes
//	 the page up and page down buttons by default.
	public static JScrollPane createPagingScrollPaneForTable(JTable jt) 
	{
	 JScrollPane jsp = new JScrollPane(jt);
	 TableModel tmodel = jt.getModel();
	 
	 // Don't choke if this is called on a regular table . . .
	 if (! (tmodel instanceof MySQLPagingModel || tmodel instanceof MSSQLPagingModel || tmodel instanceof PagingModel)) 
	 {
	   return jsp;
	 }

	 // Okay, go ahead and build the real scrollpane
	 final MSSQLPagingModel model = (MSSQLPagingModel)tmodel;
	 final JButton upButton = new JButton(/*new ArrowIcon(ArrowIcon.UP)*/"UP");
	 upButton.setEnabled(false);  // starts off at 0, so can't go up
	 final JButton downButton = new JButton(/*new ArrowIcon(ArrowIcon.DOWN)*/"DOWN");
	 if (model.getPageCount() <= 1) 
	 {
	   downButton.setEnabled(false);  // One page...can't scroll down
	 }

	 upButton.addActionListener(new ActionListener() 
	 {
	   public void actionPerformed(ActionEvent ae) 
	   {
	     model.pageUp();

	     // If we hit the top of the data, disable the up button.
	     if (model.getPageOffset() == 0) 
	     {
	       upButton.setEnabled(false);
	     }
	     downButton.setEnabled(true);
	   }
	 } );

	 downButton.addActionListener(new ActionListener() {
	   public void actionPerformed(ActionEvent ae) {
	     model.pageDown();

	     // If we hit the bottom of the data, disable the down button.
	     if (model.getPageOffset() == (model.getPageCount() - 1)) {
	       downButton.setEnabled(false);
	     }
	     upButton.setEnabled(true);
	   }
	 } );

	 // Turn on the scrollbars; otherwise we won't get our corners.
	 jsp.setVerticalScrollBarPolicy
	     (ScrollPaneConstants.VERTICAL_SCROLLBAR_ALWAYS);
	 jsp.setHorizontalScrollBarPolicy
	     (ScrollPaneConstants.HORIZONTAL_SCROLLBAR_ALWAYS);

	 // Add in the corners (page up/down).
	 jsp.setCorner(ScrollPaneConstants.UPPER_RIGHT_CORNER, upButton);
	 jsp.setCorner(ScrollPaneConstants.LOWER_RIGHT_CORNER, downButton);

	 return jsp;
	}
}
```

Der obige code funktioniert leider nicht mit MySQL. Da kommt immernoch dieser Speichererror. Also hab ich für MySQL ein eigenes PageingModel geschrieben:


```
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.sql.SQLException;
import java.sql.Statement;

import javax.swing.table.*;
import javax.swing.*;
import java.awt.event.*;

public class MySQLPagingModel extends AbstractTableModel
{
	private Connection conn;
	private ResultSet rs;
	private ResultSetMetaData rsmd;
	private String statement;
	
	protected int pageSize;
	protected int pageOffset;
	private int row;
	private int rowCount;
	private int columnCount;
	private String[] columnNames;
	protected String[][] data;

	public MySQLPagingModel(String statement)
	{
		this.statement = statement;
		conn = DBConnection.getConnection();
		row = 0;
		pageOffset = 0;
		pageSize = 100;
		try 
		{
			rs = DBConnection.executeQuery("SELECT * FROM ("+statement+") AS tmp LIMIT 0, "+pageSize);
			rsmd = rs.getMetaData();
			//Zeilenzahl berechnen
			Statement stm2 = conn.createStatement();
			ResultSet rs2 = stm2.executeQuery("SELECT COUNT(*) FROM ("+statement+") AS tmp");
			rs2.next();
			rowCount = rs2.getInt(1);
			rs2.close();
			stm2.close();
			//////
			columnCount = rsmd.getColumnCount();
			columnNames = new String[columnCount];
			for (int i=0; i<columnCount; i++)
			{
				columnNames[i] = rsmd.getColumnName(i+1);
			}
			data = new String[pageSize][columnCount];
			int i=0;
			while (rs.next())
			{
				for (int j=0; j<columnCount; j++)
				{
					data[i][j]= rs.getString(j+1);					
				}
				i++;
			}
			rs.close();
		}
		catch (SQLException e) 
		{
			e.printStackTrace();
		}
	}

	//Return values appropriate for the visible table part.
	public int getRowCount() { return Math.min(pageSize, rowCount); }
	public int getColumnCount() { return columnCount; }

	// Work only on the visible part of the table.
	public Object getValueAt(int row, int col) 
	{
	 //int realRow = row + (pageOffset * pageSize);
	 return data[row][col];
	}
	public String getColumnName(int col) 
	{
	 return columnNames[col];
	}
	//Use this method to figure out which page you are on.
	public int getPageOffset() { return pageOffset; }

	public int getPageCount() 
	{ 
	 return (int)Math.ceil((double)rowCount / pageSize);
	}

	// Use this method if you want to know how big the real table is . . . we
	// could also write "getRealValueAt()" if needed.
	public int getRealRowCount() 
	{
	 return rowCount;
	}

	public int getPageSize() { return pageSize; }
	/*
	public void setPageSize(int s) 
	{ 
	 if (s == pageSize) { return; }
	 int oldPageSize = pageSize;
	 pageSize = s;
	 pageOffset = (oldPageSize * pageOffset) / pageSize;
	 fireTableDataChanged();
	*/
	/*
	 if (pageSize < oldPageSize) {
	   fireTableRowsDeleted(pageSize, oldPageSize - 1);
	 }
	 else {
	   fireTableRowsInserted(oldPageSize, pageSize - 1);
	 }
	*/
	//}

	//Update the page offset and fire a data changed (all rows).
	public void pageDown() 
	{
	 System.out.println(pageOffset);
	 if (pageOffset < getPageCount() - 1) 
	 {
	   pageOffset++;
	   try 
	   {
		   rs = DBConnection.executeQuery("SELECT * FROM ("+statement+") AS tmp LIMIT "+(pageOffset*pageSize)+", "+pageSize);
		   int i=0;
		   while (rs.next() /*&& i<pageSize*/)
		   {
			   for (int j=0; j<columnCount; j++)
			   {
				   data[i][j] = rs.getString(j+1);
			   }
			   i++;
		   }
		   for (int k=i; k<pageSize; k++)
		   {
			   for (int j=0; j<columnCount; j++)
			   {
				   data[k][j] = "";
			   }
		   }
	   }
	   catch (SQLException e)
	   {
		   e.printStackTrace();
	   }
	   fireTableDataChanged();
	 }
	}

//	 Update the page offset and fire a data changed (all rows).
	public void pageUp() 
	{
	 System.out.println(pageOffset);
	 if (pageOffset > 0) 
	 {
	   pageOffset--;
	   try 
	   {
		   String test = "SELECT * FROM ("+statement+") AS tmp LIMIT "+(pageOffset*pageSize)+", "+pageSize;
		   rs = DBConnection.executeQuery(test);
		   System.out.println(test);
		   int i=0;
		   while (rs.next() /*&& i<pageSize*/)
		   {
			   for (int j=0; j<columnCount; j++)
			   {
				   data[i][j] = rs.getString(j+1);
			   }
			   i++;
		   }
	   }
	   catch (SQLException e)
	   {
		   e.printStackTrace();
	   }
	   fireTableDataChanged();
	 }
	}

//	 We provide our own version of a scrollpane that includes
//	 the page up and page down buttons by default.
	public static JScrollPane createPagingScrollPaneForTable(JTable jt) 
	{
	 JScrollPane jsp = new JScrollPane(jt);
	 TableModel tmodel = jt.getModel();
	 
	 // Don't choke if this is called on a regular table . . .
	 if (! (tmodel instanceof MySQLPagingModel || tmodel instanceof PagingModel)) 
	 {
	   return jsp;
	 }

	 // Okay, go ahead and build the real scrollpane
	 final MySQLPagingModel model = (MySQLPagingModel)tmodel;
	 final JButton upButton = new JButton(/*new ArrowIcon(ArrowIcon.UP)*/"UP");
	 upButton.setEnabled(false);  // starts off at 0, so can't go up
	 final JButton downButton = new JButton(/*new ArrowIcon(ArrowIcon.DOWN)*/"DOWN");
	 if (model.getPageCount() <= 1) 
	 {
	   downButton.setEnabled(false);  // One page...can't scroll down
	 }

	 upButton.addActionListener(new ActionListener() 
	 {
	   public void actionPerformed(ActionEvent ae) 
	   {
	     model.pageUp();

	     // If we hit the top of the data, disable the up button.
	     if (model.getPageOffset() == 0) 
	     {
	       upButton.setEnabled(false);
	     }
	     downButton.setEnabled(true);
	   }
	 } );

	 downButton.addActionListener(new ActionListener() {
	   public void actionPerformed(ActionEvent ae) {
	     model.pageDown();

	     // If we hit the bottom of the data, disable the down button.
	     if (model.getPageOffset() == (model.getPageCount() - 1)) {
	       downButton.setEnabled(false);
	     }
	     upButton.setEnabled(true);
	   }
	 } );

	 // Turn on the scrollbars; otherwise we won't get our corners.
	 jsp.setVerticalScrollBarPolicy
	     (ScrollPaneConstants.VERTICAL_SCROLLBAR_ALWAYS);
	 jsp.setHorizontalScrollBarPolicy
	     (ScrollPaneConstants.HORIZONTAL_SCROLLBAR_ALWAYS);

	 // Add in the corners (page up/down).
	 jsp.setCorner(ScrollPaneConstants.UPPER_RIGHT_CORNER, upButton);
	 jsp.setCorner(ScrollPaneConstants.LOWER_RIGHT_CORNER, downButton);

	 return jsp;
	}
}
```

So das funktioniert zwar Theoretisch. Aber jeder Pagewechsel dauert ewig (so 30 - 45 sec) bei einer PageSize von 100. Irgendwie scheint der MySQL-Server die ergebnisse aber zu cachen oder irgendwelche temporären StoredProcedures anzulegen oder weiß der Geier. Auf jeden Fall geht das Angucken von Seiten, die ich schonmal angeguckt hab richtig schön schnell, auch wenn ich mein Prog zwischendurch schliesse und wieder neu öffne.

Meine Frage wäre, wieso ist das so  lahm? Wie kann ich das schneller machen?

Und so aus mehr allgemeinem Interesse. Wie speichert der MySQL-Server welche Daten, dass ich so schnell darauf zugriff habe, wenn ich sie vorher schon irgendwann mal abgerufen habe? (Der kann ja nicht hunderte von Ergebnissmengen cachen oder etwa doch?)


----------



## limago (14. September 2007)

Hi,

hier mal ein Schnellschuss aus der Hüfte... Ich würde die Statements, wenn es geht in PreparedStatements umwandeln. Dann werden Sie oft deutlich schneller ausgeführt. 

Soweit ich weiss, muss bei einem Statement immer der SQL-String von dem DBMS geparst und ein Ausführungsplan erstellt werden. Wenn Du Glück hast, wird das Statement danach gecached. 

Ein PreparedStatement macht das gleich und landet gleich im Cache. Es kann parametriesiert werden und sollte deswegen die Geschwindigkeit erhöhen...

Versuchs mal.

Gruss


----------



## tekilla209 (14. September 2007)

Logisch da hätt ich auch mal drauf kommen können.
Aber, ich hab das jetzt gemacht und scheinbar scheinen preparedStatements in verbindung mit LIMIT nicht viel zu nützen. Denn jetzt hab ich ca. 15sec pro zu Ladender Seite. Allerding cacht der SQLServer jetzt nix mehr und so dauert der seitenaufbau immer 15sec.


```
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.sql.SQLException;
import java.sql.Statement;

import javax.swing.table.*;
import javax.swing.*;
import java.awt.event.*;

public class MySQLPagingModel extends AbstractTableModel
{
	private Connection conn;
	private PreparedStatement preparedStatement;
	private ResultSet rs;
	private ResultSetMetaData rsmd;
	private String statement;
	
	protected int pageSize;
	protected int pageOffset;
	private int row;
	private int rowCount;
	private int columnCount;
	private String[] columnNames;
	protected String[][] data;

	public MySQLPagingModel(String statement)
	{
		this.statement = statement;
		conn = DBConnection.getConnection();
		row = 0;
		pageOffset = 0;
		pageSize = 100;
		try 
		{
			preparedStatement = conn.prepareStatement("SELECT * FROM ("+statement+") AS tmp LIMIT ?, ?");
			rs = DBConnection.executeQuery("SELECT * FROM ("+statement+") AS tmp LIMIT 0, "+pageSize);
			rsmd = rs.getMetaData();
			//Zeilenzahl berechnen
			Statement stm2 = conn.createStatement();
			ResultSet rs2 = stm2.executeQuery("SELECT COUNT(*) FROM ("+statement+") AS tmp");
			rs2.next();
			rowCount = rs2.getInt(1);
			rs2.close();
			stm2.close();
			//////
			columnCount = rsmd.getColumnCount();
			columnNames = new String[columnCount];
			for (int i=0; i<columnCount; i++)
			{
				columnNames[i] = rsmd.getColumnName(i+1);
			}
			data = new String[pageSize][columnCount];
			int i=0;
			while (rs.next())
			{
				for (int j=0; j<columnCount; j++)
				{
					data[i][j]= rs.getString(j+1);					
				}
				i++;
			}
			rs.close();
		}
		catch (SQLException e) 
		{
			e.printStackTrace();
		}
	}

	//Return values appropriate for the visible table part.
	public int getRowCount() { return Math.min(pageSize, rowCount); }
	public int getColumnCount() { return columnCount; }

	// Work only on the visible part of the table.
	public Object getValueAt(int row, int col) 
	{
	 return data[row][col];
	}
	public String getColumnName(int col) 
	{
	 return columnNames[col];
	}
	//Use this method to figure out which page you are on.
	public int getPageOffset() { return pageOffset; }

	public int getPageCount() 
	{ 
	 return (int)Math.ceil((double)rowCount / pageSize);
	}

	// Use this method if you want to know how big the real table is . . . we
	// could also write "getRealValueAt()" if needed.
	public int getRealRowCount() 
	{
	 return rowCount;
	}

	public int getPageSize() { return pageSize; }
	/*
	public void setPageSize(int s) 
	{ 
	 if (s == pageSize) { return; }
	 int oldPageSize = pageSize;
	 pageSize = s;
	 pageOffset = (oldPageSize * pageOffset) / pageSize;
	 fireTableDataChanged();
	*/
	/*
	 if (pageSize < oldPageSize) {
	   fireTableRowsDeleted(pageSize, oldPageSize - 1);
	 }
	 else {
	   fireTableRowsInserted(oldPageSize, pageSize - 1);
	 }
	*/
	//}

	//Update the page offset and fire a data changed (all rows).
	public void pageDown() 
	{
	 System.out.println(pageOffset);
	 if (pageOffset < getPageCount() - 1) 
	 {
	   pageOffset++;
	   try 
	   {
		   preparedStatement.setInt(1, (pageOffset*pageSize));
		   preparedStatement.setInt(2, pageSize);
		   rs = preparedStatement.executeQuery();
		   int i=0;
		   while (rs.next() /*&& i<pageSize*/)
		   {
			   for (int j=0; j<columnCount; j++)
			   {
				   data[i][j] = rs.getString(j+1);
			   }
			   i++;
		   }
		   for (int k=i; k<pageSize; k++)
		   {
			   for (int j=0; j<columnCount; j++)
			   {
				   data[k][j] = "";
			   }
		   }
	   }
	   catch (SQLException e)
	   {
		   e.printStackTrace();
	   }
	   fireTableDataChanged();
	 }
	}

//	 Update the page offset and fire a data changed (all rows).
	public void pageUp() 
	{
	 System.out.println(pageOffset);
	 if (pageOffset > 0) 
	 {
	   pageOffset--;
	   try 
	   {
		   //String test = statement+" LIMIT "+(pageOffset*pageSize)+", "+((pageOffset*pageSize)+pageSize);
		   //String test = "SELECT * FROM ("+statement+") AS tmp LIMIT "+(pageOffset*pageSize)+", "+pageSize;
		   //rs = DBConnection.executeQuery(test);
		   //System.out.println(test);
		   preparedStatement.setInt(1, (pageOffset*pageSize));
		   preparedStatement.setInt(2, pageSize);
		   rs = preparedStatement.executeQuery();
		   int i=0;
		   while (rs.next() /*&& i<pageSize*/)
		   {
			   for (int j=0; j<columnCount; j++)
			   {
				   data[i][j] = rs.getString(j+1);
			   }
			   i++;
		   }
	   }
	   catch (SQLException e)
	   {
		   e.printStackTrace();
	   }
	   fireTableDataChanged();
	 }
	}

//	 We provide our own version of a scrollpane that includes
//	 the page up and page down buttons by default.
	public static JScrollPane createPagingScrollPaneForTable(JTable jt) 
	{
	 JScrollPane jsp = new JScrollPane(jt);
	 TableModel tmodel = jt.getModel();
	 
	 // Don't choke if this is called on a regular table . . .
	 if (! (tmodel instanceof MySQLPagingModel || tmodel instanceof PagingModel)) 
	 {
	   return jsp;
	 }

	 // Okay, go ahead and build the real scrollpane
	 final MySQLPagingModel model = (MySQLPagingModel)tmodel;
	 final JButton upButton = new JButton(/*new ArrowIcon(ArrowIcon.UP)*/"UP");
	 upButton.setEnabled(false);  // starts off at 0, so can't go up
	 final JButton downButton = new JButton(/*new ArrowIcon(ArrowIcon.DOWN)*/"DOWN");
	 if (model.getPageCount() <= 1) 
	 {
	   downButton.setEnabled(false);  // One page...can't scroll down
	 }

	 upButton.addActionListener(new ActionListener() 
	 {
	   public void actionPerformed(ActionEvent ae) 
	   {
	     model.pageUp();

	     // If we hit the top of the data, disable the up button.
	     if (model.getPageOffset() == 0) 
	     {
	       upButton.setEnabled(false);
	     }
	     downButton.setEnabled(true);
	   }
	 } );

	 downButton.addActionListener(new ActionListener() {
	   public void actionPerformed(ActionEvent ae) {
	     model.pageDown();

	     // If we hit the bottom of the data, disable the down button.
	     if (model.getPageOffset() == (model.getPageCount() - 1)) {
	       downButton.setEnabled(false);
	     }
	     upButton.setEnabled(true);
	   }
	 } );

	 // Turn on the scrollbars; otherwise we won't get our corners.
	 jsp.setVerticalScrollBarPolicy
	     (ScrollPaneConstants.VERTICAL_SCROLLBAR_ALWAYS);
	 jsp.setHorizontalScrollBarPolicy
	     (ScrollPaneConstants.HORIZONTAL_SCROLLBAR_ALWAYS);

	 // Add in the corners (page up/down).
	 jsp.setCorner(ScrollPaneConstants.UPPER_RIGHT_CORNER, upButton);
	 jsp.setCorner(ScrollPaneConstants.LOWER_RIGHT_CORNER, downButton);

	 return jsp;
	}
}
```


----------



## limago (14. September 2007)

Hi,

dann weiss ich auch nicht weiter. Hört sich so an, als müsstest Du tief in den MySql abtauchen und das hat mit Java nix mehr zu tun. Die 15 bzw. 30 Sekunden wundern mich ehrlich gesagt ein wenig. Hast Du das mal nativ mit der MySQL-Console validiert?

Kannst Du die Daten denn nicht selbst cachen?

Wie auch immer, wenn Du eine Lösung findest, wäre ich sehr interessiert...

Gruß


----------



## tekilla209 (17. September 2007)

So, hab jez ne einigermaßen akzeptable Lösung gefunden.
Hab statt "Select * From ("+statement+") as tmp LIMIT blabla.
Einfach nur "("+statement+") Limit blabla" gemacht. Jetzt gehts schön schnell. Aber die letzte Page wird leider bis zur pageSize aufgefüllt.
Wenn ich also z.B. (Select * From tabelle LIMIT 0, 150) LIMIT 0, 200; ausführe bekomme ich leider 200 ergebnisse und keine 150.
Da das aber nur in der Anzeige so ist und zum Weiterverarbeiten das original Statement was der user eingegeben hat ausgeführt wird ist das jetzt nicht so wichtig.

hier nochmal komplett:

```
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.sql.SQLException;
import java.sql.Statement;
//import java.util.List;

import javax.swing.table.*;
import javax.swing.*;
import java.awt.event.*;
//import java.awt.*;

public class MySQLPagingModel extends AbstractTableModel
{
	private Connection conn;
	private PreparedStatement preparedStatement;
	private ResultSet rs;
	private ResultSetMetaData rsmd;
	private String statement;
	
	protected int pageSize;
	protected int pageOffset;
	private int row;
	private int rowCount;
	private int columnCount;
	private String[] columnNames;
	//protected Record[] data;
	protected String[][] data;

	public MySQLPagingModel(String statement)
	{
		this.statement = statement;
		conn = DBConnection.getConnection();
		row = 0;
		pageOffset = 0;
		pageSize = 100;
		try 
		{
			preparedStatement = conn.prepareStatement("("+statement+") LIMIT ?, ?");
			rs = DBConnection.executeQuery("SELECT * FROM ("+statement+") AS tmp LIMIT 0, "+pageSize);
			//rs = DBConnection.executeQuery(statement);
			rsmd = rs.getMetaData();
			//Zeilenzahl berechnen
			Statement stm2 = conn.createStatement();
			ResultSet rs2 = stm2.executeQuery("SELECT COUNT(*) FROM ("+statement+") AS tmp");
			rs2.next();
			rowCount = rs2.getInt(1);
			rs2.close();
			stm2.close();
			//////
			columnCount = rsmd.getColumnCount();
			columnNames = new String[columnCount];
			for (int i=0; i<columnCount; i++)
			{
				columnNames[i] = rsmd.getColumnName(i+1);
			}
			data = new String[pageSize][columnCount];
			int i=0;
			while (rs.next())
			{
				for (int j=0; j<columnCount; j++)
				{
					data[i][j]= rs.getString(j+1);					
				}
				i++;
			}
			rs.close();
		}
		catch (SQLException e) 
		{
			e.printStackTrace();
		}
	}

	//Return values appropriate for the visible table part.
	public int getRowCount() { return Math.min(pageSize, rowCount); }
	public int getColumnCount() { return columnCount; }

	// Work only on the visible part of the table.
	public Object getValueAt(int row, int col) 
	{
	 //int realRow = row + (pageOffset * pageSize);
	 return data[row][col];
	}
	public String getColumnName(int col) 
	{
	 return columnNames[col];
	}
	//Use this method to figure out which page you are on.
	public int getPageOffset() { return pageOffset; }

	public int getPageCount() 
	{ 
	 return (int)Math.ceil((double)rowCount / pageSize);
	}

	// Use this method if you want to know how big the real table is . . . we
	// could also write "getRealValueAt()" if needed.
	public int getRealRowCount() 
	{
	 return rowCount;
	}

	public int getPageSize() { return pageSize; }
	/*
	public void setPageSize(int s) 
	{ 
	 if (s == pageSize) { return; }
	 int oldPageSize = pageSize;
	 pageSize = s;
	 pageOffset = (oldPageSize * pageOffset) / pageSize;
	 fireTableDataChanged();
	*/
	/*
	 if (pageSize < oldPageSize) {
	   fireTableRowsDeleted(pageSize, oldPageSize - 1);
	 }
	 else {
	   fireTableRowsInserted(oldPageSize, pageSize - 1);
	 }
	*/
	//}

	//Update the page offset and fire a data changed (all rows).
	public void pageDown() 
	{
	 System.out.println(pageOffset);
	 if (pageOffset < getPageCount() - 1) 
	 {
	   pageOffset++;
	   try 
	   {
		   //rs = DBConnection.executeQuery(statement+" LIMIT "+(pageOffset*pageSize)+", "+((pageOffset*pageSize)+pageSize));
		   //preparedStatement = conn.prepareStatement("SELECT * FROM ("+statement+") AS tmp LIMIT ?, ?");
		   preparedStatement.setInt(1, (pageOffset*pageSize));
		   preparedStatement.setInt(2, pageSize);
		   //rs = DBConnection.executeQuery("SELECT * FROM ("+statement+") AS tmp LIMIT "+(pageOffset*pageSize)+", "+pageSize);
		   rs = preparedStatement.executeQuery();
		   int i=0;
		   while (rs.next() /*&& i<pageSize*/)
		   {
			   for (int j=0; j<columnCount; j++)
			   {
				   data[i][j] = rs.getString(j+1);
			   }
			   i++;
		   }
		   rs.close();
		   for (int k=i; k<pageSize; k++)
		   {
			   for (int j=0; j<columnCount; j++)
			   {
				   data[k][j] = "";
			   }
		   }
	   }
	   catch (SQLException e)
	   {
		   e.printStackTrace();
	   }
	   fireTableDataChanged();
	 }
	}

//	 Update the page offset and fire a data changed (all rows).
	public void pageUp() 
	{
	 System.out.println(pageOffset);
	 if (pageOffset > 0) 
	 {
	   pageOffset--;
	   try 
	   {
		   //String test = statement+" LIMIT "+(pageOffset*pageSize)+", "+((pageOffset*pageSize)+pageSize);
		   //String test = "SELECT * FROM ("+statement+") AS tmp LIMIT "+(pageOffset*pageSize)+", "+pageSize;
		   //rs = DBConnection.executeQuery(test);
		   //System.out.println(test);
		   preparedStatement.setInt(1, (pageOffset*pageSize));
		   preparedStatement.setInt(2, pageSize);
		   rs = preparedStatement.executeQuery();
		   int i=0;
		   while (rs.next() /*&& i<pageSize*/)
		   {
			   for (int j=0; j<columnCount; j++)
			   {
				   data[i][j] = rs.getString(j+1);
			   }
			   i++;
		   }
		   rs.close();
	   }
	   catch (SQLException e)
	   {
		   e.printStackTrace();
	   }
	   fireTableDataChanged();
	 }
	}

//	 We provide our own version of a scrollpane that includes
//	 the page up and page down buttons by default.
	public static JScrollPane createPagingScrollPaneForTable(JTable jt) 
	{
	 JScrollPane jsp = new JScrollPane(jt);
	 TableModel tmodel = jt.getModel();
	 
	 // Don't choke if this is called on a regular table . . .
	 if (! (tmodel instanceof MySQLPagingModel || tmodel instanceof PagingModel)) 
	 {
	   return jsp;
	 }

	 // Okay, go ahead and build the real scrollpane
	 final MySQLPagingModel model = (MySQLPagingModel)tmodel;
	 final JButton upButton = new JButton(/*new ArrowIcon(ArrowIcon.UP)*/"UP");
	 upButton.setEnabled(false);  // starts off at 0, so can't go up
	 final JButton downButton = new JButton(/*new ArrowIcon(ArrowIcon.DOWN)*/"DOWN");
	 if (model.getPageCount() <= 1) 
	 {
	   downButton.setEnabled(false);  // One page...can't scroll down
	 }

	 upButton.addActionListener(new ActionListener() 
	 {
	   public void actionPerformed(ActionEvent ae) 
	   {
	     model.pageUp();

	     // If we hit the top of the data, disable the up button.
	     if (model.getPageOffset() == 0) 
	     {
	       upButton.setEnabled(false);
	     }
	     downButton.setEnabled(true);
	   }
	 } );

	 downButton.addActionListener(new ActionListener() {
	   public void actionPerformed(ActionEvent ae) {
	     model.pageDown();

	     // If we hit the bottom of the data, disable the down button.
	     if (model.getPageOffset() == (model.getPageCount() - 1)) {
	       downButton.setEnabled(false);
	     }
	     upButton.setEnabled(true);
	   }
	 } );

	 // Turn on the scrollbars; otherwise we won't get our corners.
	 jsp.setVerticalScrollBarPolicy
	     (ScrollPaneConstants.VERTICAL_SCROLLBAR_ALWAYS);
	 jsp.setHorizontalScrollBarPolicy
	     (ScrollPaneConstants.HORIZONTAL_SCROLLBAR_ALWAYS);

	 // Add in the corners (page up/down).
	 jsp.setCorner(ScrollPaneConstants.UPPER_RIGHT_CORNER, upButton);
	 jsp.setCorner(ScrollPaneConstants.LOWER_RIGHT_CORNER, downButton);

	 return jsp;
	}
}
```


----------



## tekilla209 (17. September 2007)

Eine Frage hätt ich da noch.
Wo hastn Du das ArrowIcon her?
new ArrowIcon(ArrowIcon.UP) kennt eclipse bei mir nicht

hab was gefunden:
org.gui4j.core.swing.calendar;

meintest Du das?


----------



## limago (17. September 2007)

Ja, es gibt aber wohl noch mehr Varianten im Netz..

Schöne Lösung! Hast gut gekämpft....

Grüße


----------



## Milkabär (14. Februar 2010)

Ich habe gute Erfahrungen mit gc() gemacht.  Nachdem ich mehrere Connection-Objekte angelegt hatte ist mir der Speicher übergelaufen, und ich war zu dämlich, die Sache mit close()-Statements in den Griff zu bekommen. Ich habe den Fehler dann durch ein try-catch lokalisiert, und im catch-Block den Garbage-Collector aufgerufen. Vom Programmierstil sicherlich quick&dirty, aber es funktioniert!


----------



## kritzel3000 (6. Februar 2012)

Hallo
ich hab eine frage:
Muß ich unbedingt den Zeiger auf null setzen, damit der GC das löscht?

Beispiel:
ich gebe

```
JLabel label = new JLabel();
//label = null;
label = new JLabel();
```
ein.
wird das alte Objekt dann trotzdem vom GC gelöscht?


----------



## limago (6. Februar 2012)

Hallo

Objekte werden gelöscht, wenn sie nicht mehr erreichbar sind. Du musst den Zeiger nicht auf "null" setzen. Wenn keine zweite Referenz auf das JLabel verweist, wird es weggeräumt.


----------



## sheel (6. Februar 2012)

Also ja, dein Code ist so in Ordnung.

Falls du die Frage deswegen stellst, weil es nicht gelöscht wird:
Es muss nicht sofort passieren.
Der GC kann das machen, wann er will.

PS: Willkommen bei tutorials.de


----------



## j2se (10. Februar 2012)

Verwende die Klasse StringBuilder statt String!


----------



## Akeshihiro (10. Februar 2012)

Hä? Und wo ist da jetzt der Zusammenhang?


----------



## j2se (10. Februar 2012)

Mein Post bezieht sich auf


```
FileReader fr = new FileReader(importFile);
String line ="";
char c;
int i=0;
do 
{
    c = (char)fr.read();
    line += c;
    i++;
}
while (c != '\n');
```


----------



## Akeshihiro (10. Februar 2012)

Der Beitrag war von 2007 und ist auf der ersten Seite ^^ Ich dachte, das hatte aktuellen Kontext ...


----------

