PDA

Archiv verlassen und diese Seite im Standarddesign anzeigen : Java: Ram verbrauch optimieren...


ooAlbert
2007-02-24, 17:42:42
Hi,

ich hab mal ne Frage zum reduzieren des benötigten Arbeitsspeichers für Java-Applets und PRogramme.

Gibt es da Möglichkeiten oder Tools die sowas Optimieren in hinblich auch geringen Verbrauch? Denn manche meiner Anwendungen ziehen sich ca. 30MB obwohl die an sich nur in der Konsole arbeiten. Ich vermute das liegt an den bibliothekten die alle pauschal reingeladen werden.

mfg

Senior Sanchez
2007-02-24, 17:52:04
Naja, im Grunde liegt es erstmal an der Standard VM Einstellung, sprich wieviel Speicher der Heap maximal allokieren darf, was seine Initialsize ist usw.

Tools die direkt optimieren, gibt es meiner Ansicht nach nicht. Höchstens Profiler, die dich als Programmierer dabei unterstützen, Speicherfresser zu finden, sodass du die optimieren kannst.

Aber soviel Gedanken würde ich mir in der Regel um den RAM-Verbrauch gar nicht machen, es sei denn du hast entsprechende Zielvorgaben.

Gast
2007-02-24, 18:00:40
nimm nen Java->C++-Compiler

Monger
2007-02-24, 18:05:39
Bei allen Sprachen mit Garbage Collection (wie eben Java) gilt: versuch nicht klüger als der Garbage Collector zu sein!

Wenn wirklich mal der Speicher knapp wird, ist es Aufgabe der Runtime, unnötigen Ballast wegzuräumen. Was da im Hintergrund passiert, geht dich nichts an. Als Entwickler solltest du dir Gedanken darüber machen, ob dein Programm wirklich genau das macht was es soll, ob vielleicht doch noch Beziehungen zwischen Daten bestehen die eigentlich nichts miteinander zu tun haben, und ob andere Datenstrukturen für diesen oder jenen Anwendungszweck nicht vielleicht doch besser geeignet wären.
Aber lass um Himmels Willen die Runtime in Ruhe!

SavageX
2007-02-24, 20:02:40
nimm nen Java->C++-Compiler

Dann läuft immer noch ein leckerer Garbage-Collector mit. Ich sehe ad-hoc nicht, wie das Speicher spart.

Trap
2007-02-24, 21:32:43
Gibt es da Möglichkeiten oder Tools die sowas Optimieren in hinblich auch geringen Verbrauch? Denn manche meiner Anwendungen ziehen sich ca. 30MB obwohl die an sich nur in der Konsole arbeiten.
Womit guckst du den Arbeitsspeicherverbrauch an?

Bei eine Java-Anwendung ist das einzig sinnvolle Tool dafür etwas, das das Profiler-Interface der Java-VM benutzt. Den Verbrauch der VM kannst du sowieso nicht beeinflussen und nur das Profiler-Interface gibt Daten über den tatsächlichen Verbrauch der Anwendung.

HajottV
2007-02-25, 04:08:47
Gibt es da Möglichkeiten oder Tools die sowas Optimieren in hinblich auch geringen Verbrauch?

Ein paar Sachen kann man schon machen:


Strings belegen relativ viel Platz - rechne mal grob mit 40 + 2 * Stringlänge in Bytes (für Suns JVM unter 32 Bit). Oft ist es so, daß man den gleichen String mehrmals benutzt. Da kann es sinnvoll sein, diese Strings zusammenzuführen. Dazu gibt es die intern() Methode. Die stellt sicher als Du nicht die gleichen Strings sondern die selben Strings hast. (Vorsicht! intern() ist sehr lahm, es empfiehlt sich da durchaus mit einem Cache zu arbeiten, wenn man mit richtig vielen Strings arbeitet)
Eine Sache, die oft gemacht wird, weil sie so praktisch ist, ist es, primitive Datentypen in Objekte zu verpacken und dann z.B. in eine ArrayList zu stecken. Ganz pöse, wenn man richtig lange Listen benutzt! Ein Integer Objekt benötigt 16 Bytes, wenn Du Suns JVM (32 Bit) benutzt - unt es ist vermutlich noch schlimmer unter 64 Bit. Wenn man also Integer Objekte in eine ArrayList packt, braucht man 24 + 20 * Länge Bytes. Ein int[] Array kommt mit etwa 16 + 4 * Länge Bytes aus.


Das ist so das, was mir spontan einfällt.

Gruß

Jörg

ScottManDeath
2007-02-25, 07:38:03
Oder man sollte drauf aufpassen, dass man Speicher freigibt, sobald man ihn nicht mehr beötigt, in dem man die Referenzen auf null setzt. Ansonsten passiert es einem schnell dass man referenzierte Speicherleichen rumliegen hat, weil der GC diese nicht aufräumen kann, da sie noch referenziert werden.

PH4Real
2007-02-25, 12:21:25
Mit JConsole (http://java.sun.com/developer/technicalArticles/J2SE/jconsole.html) kannst Du schonmal den genauen Speicherverbrauch überprüfen. Ist beim JDK ab 1.5 dabei. Ab 1.6 auch noch komfortabler zu nutzen.

Ansonsten gibt es ganz gute kostenlose Profiler. Im Netbeans Profiler (http://profiler.netbeans.org/) kannst Du dir zum Beispiel die Speichergröße aller erstellten Objekte geordnet anzeigen lassen. Eclipse hat auch ein Profiler (TPTP), aber den fand ich bisher nicht so stabil.

ScottManDeath kann ich nur zustimmen. Die Referenzen müssen natürlich auch in Java freigegeben werden, weil sonst der GC nicht aufräumen kann. Explizit auf null setzen ist aber nicht nötig, wenn du den Sichtbarkeitsbereich eh verläßt. Vorsicht mit solchen Patterns wie Singletons... mit denen kann man sich fürchterliche Speicherlöcher bauen (ich ärger mich immer noch :biggrin:).

ooAlbert
2007-02-25, 16:40:21
naja ich hab halkt einfach mal geschaut was mir der taskmanager angezeigt hatte :) und im beispiel eines applets waren da 20MB für den Browser veranschlagt und etwa 30MB fürs applet.

Strings werden zwar benutzt aber in geringem maße und immer wider überschrieben. Ansonsten ist es halt eine recht simple anwendung mich hat das nur gewundert das es sich so aufbläht.

Abnaxos
2007-02-25, 17:05:51
Strings belegen relativ viel Platz - rechne mal grob mit 40 + 2 * Stringlänge in Bytes (für Suns JVM unter 32 Bit).
Das kommt sehr darauf an, wie der String entstanden ist. Die meisten Methoden von String (z.B. substring()) geben einen String zurück, der intern dasselbe char-Array verwendet. Je nachdem, was du machst, erhöhst du also den Speicherverbrauch eher, wenn du intern() oder new String() verwendest. Das geht allerdings auch umgekehrt: Wenn du einen 2000-Zeichen-String hast, via substring() 2 davon nimmst und den Rest wegwirfst, dann new String() verwenden, da ansonsten das ganze 2000-Zeichen-Char-Array weiter im Speicher gehalten wird. intern() macht eigentlich nur in Ausnahmefällen Sinn, wobei mir in über 8 Jahren Java-Entwicklung kein einziger solcher Fall begegnet ist.

Oder man sollte drauf aufpassen, dass man Speicher freigibt, sobald man ihn nicht mehr beötigt, in dem man die Referenzen auf null setzt. Ansonsten passiert es einem schnell dass man referenzierte Speicherleichen rumliegen hat, weil der GC diese nicht aufräumen kann, da sie noch referenziert werden.
Wenn sie noch referenziert werden, sollen sie ja auch nicht aufgeräumt werden. Mit Maps kann es manchmal Speicherlöcher geben, ansonsten sollte man sich über den GC keine Gedanken machen, der macht das schon gut -- es ist der Job des GC dich von solchen Details zu befreien, also lass ihn seinen Job machen. ;) Falls du auf zirkuläre Referenzen hinaus willst: Java hat selbstverständlich einen echten GC, nicht, wie z.B. Python, einen Reference Counting GC bei dem so natürlich tatsächlich Speicherlöcher entstehen können.

Köppchen
2007-02-25, 17:23:09
Wenn ich an meinen Rechner auf der Arbeit denke ist das noch im Rahmen. Jeder gestartete Websphere 6.0 Prozess zieht sich mindestens 200MB Hauptspeicher. Die Entwicklungsumgebung (RAD6) selber zieht oft noch deutlich mehr. Ehrlich gesagt kenne ich keine Java Applikation die richtig sparsam mit Hauptspeicher umgeht. Dafür kann man mit den neuesten JRE's wirklich nicht mehr über CPU Performance meckern.

Gruß Markus

ooAlbert
2007-02-26, 08:06:56
da muß ich jetzt doch nochmal nachfragen wegen den strings :)

Es soll was aus einer TXT eingelesen werden dazu wird die komplette txt mittels "vector" eingelsen und dann das letze element in einen string übergeben.

Nach einigen beobachtungen hat sich herausgestellt, das ganz allmählich der speicher gefüllt wird bis zu der gerenze des heaps ... das dauerste immerhin 14 tage ;)

Da sonst intern keine größeren datenmengen erzeugt werden vermute ich, das der vector das ganze aufbläht. Ich dachte jedoch, das sowas garnicht passsieren kann, weil der ja alle 5 min überschreiben wird durch erneutes einlesen und die TXT wiederum eine gewisse maximalgröße nicht überschreitet (ca. 50kb[8312 Zeichen]) dürfte der heap ja nie ausgenutzt werden.



hab ich da was falsch verstanden oder wie? :)

Shink
2007-02-26, 08:29:48
da muß ich jetzt doch nochmal nachfragen wegen den strings :)

Es soll was aus einer TXT eingelesen werden dazu wird die komplette txt mittels "vector" eingelsen und dann das letze element in einen string übergeben.

Nach einigen beobachtungen hat sich herausgestellt, das ganz allmählich der speicher gefüllt wird bis zu der gerenze des heaps ... das dauerste immerhin 14 tage ;)

Da sonst intern keine größeren datenmengen erzeugt werden vermute ich, das der vector das ganze aufbläht. Ich dachte jedoch, das sowas garnicht passsieren kann, weil der ja alle 5 min überschreiben wird durch erneutes einlesen und die TXT wiederum eine gewisse maximalgröße nicht überschreitet (ca. 50kb[8312 Zeichen]) dürfte der heap ja nie ausgenutzt werden.



hab ich da was falsch verstanden oder wie? :)

Da hast du wohl irgendwo noch eine Referenz auf das alte String-Objekt übersehen, sodass es nicht freigeräumt werden kann.

ooAlbert
2007-02-26, 09:32:44
hm, eigentlich nicht da eh alles in einer klasse abläuft.

mit der jconsole hab ich mir das mal angesehen und naja man siehtn sehr viel zeugs :) aber ich weiß nicht genau was was aussagt, bzw. wo man viell. variablen oder sowas sehen kann des applats.

Das einzige was man sieht ist, das der heap dauerhaft ansteigt und zwar immer um ca. 50- 100kb ...

Abnaxos
2007-02-27, 00:28:30
da muß ich jetzt doch nochmal nachfragen wegen den strings :)

Es soll was aus einer TXT eingelesen werden dazu wird die komplette txt mittels "vector" eingelsen und dann das letze element in einen string übergeben.
Ich habe noch immer keine Ahnung, wie du das implementiert hast und was es genau macht. Aber wir könnten der Sache hier tatsächlich auf der Spur sein.

Ich werde jetzt einfach genau erklären, was ich oben angesprochen habe, im Zusammenhang mit Strings (mit dem Vector sollte es eigentlich nichts zu tun haben, wenn auch von der Verwendung von Vector abzuraten ist, da diese Klasse seit ca. 6 Jahren als veraltet und als nicht mehr zu verwenden dokumentiert ist) ...

Sehen wir uns mal die Implementation von String an. Da finden wir erstmal das hier:

/** The value is used for character storage. */
private final char value[];

/** The offset is the first index of the storage that is used. */
private final int offset;

/** The count is the number of characters in the String. */
private final int count;

Ein String setzt sich also in erster Linie aus drei Komponenten zusammen: Einem Char-Array, einem Offset und einer Länge.

Weiterhin finden wir folgendes:


// Package private constructor which shares value array for speed.
String(int offset, int count, char value[]) {
this.value = value;
this.offset = offset;
this.count = count;
}

Im Package java.lang kann also ein String auch so konstruiert werden. Der Unterschied zu den Public-Konstruktoren, die ebenfalls ein char[] entgegen nehmen, legt dieser hier keine Kopie des Arrays an, sondern verwendet dasselbe Array weiter und verändert lediglich den Offset und die Länge des Strings.

Das findet z.B. in der Methode substring() Verwendung:

public String substring(int beginIndex, int endIndex) {
if (beginIndex < 0) {
throw new StringIndexOutOfBoundsException(beginIndex);
}
if (endIndex > count) {
throw new StringIndexOutOfBoundsException(endIndex);
}
if (beginIndex > endIndex) {
throw new StringIndexOutOfBoundsException(endIndex - beginIndex);
}
return ((beginIndex == 0) && (endIndex == count)) ? this :
new String(offset + beginIndex, endIndex - beginIndex, value);
}

Hier wird ein neuer String erzeugt, mit demselben String-Array als Basis, aber mit neuem Offset und neuer Länge. Das geht natürlich nur intern, ein String ist unveränderbar und soll das auch bleiben. Einen ähnlichen Konstruktor gibt es jedoch auch "public", der legt jedoch eine Kopie des Arrays an, damit es auch ganz sicher nicht mehr verändert wird. Dieser Konstruktor geht jedoch davon aus, dass alle Klassen, die Zugriff darauf haben, dafür sorgen, dass der Vertrag eingehalten wird: Das char[] darf keinesfalls mehr verändert werden, wenn es von der Sprache her auch möglich wäre.

Besonders, wenn man Strings verstückelt, ist dieses Verhalten sehr wünschenswert, weil effizient, ausserdem sind Strings in 90% der Fälle nur temporäre Objekte, es kommt also nicht drauf an. Manchmal kommt es jedoch darauf an. Wir fordern es mal heraus:

import java.util.LinkedList;

public class StringTest {

public static void main(String[] args) {
LinkedList<String> stringList = new LinkedList<String>();
for ( int counter=0; true; counter++ ) {
StringBuilder buf = new StringBuilder();
for ( int i=0; i<100000; i++ ) {
buf.append("0123456789");
}
String before = buf.toString();
String after = before.substring(before.length()-2);
System.out.println(counter
+": before.length()="+before.length()
+"; after.length()="+after.length());
// wir behalten hier 'after', das ist Sinn des Experiments:
stringList.add(after);
}
}
}

Wenn ich das laufen lasse, sieht es so aus:

rherzog@rumba:~/tmp$ javac -source 1.5 -target 1.5 StringTest.java
rherzog@rumba:~/tmp$ java -cp . StringTest
0: before.length()=1000000; after.length()=2
1: before.length()=1000000; after.length()=2
2: before.length()=1000000; after.length()=2
[...]
29: before.length()=1000000; after.length()=2
Exception in thread "main" java.lang.OutOfMemoryError: Java heap space
at java.util.Arrays.copyOfRange(Arrays.java:3209)
at java.lang.String.<init>(String.java:216)
at java.lang.StringBuilder.toString(StringBuilder.java:430)
at StringTest.main(StringTest.java:12)
rherzog@rumba:~/tmp$

Das ging jetzt aber schnell ... was ist da passiert? Ich habe einen String mit 10*100000 Zeichen angelegt. Danach habe ich mir einen Substring davon geholt, die letzten zwei Zeichen davon. Wie wir oben sehen können, wurde in diesem Moment das gesamte char[] übernommen, mit entsprechendem Offset und Länge, d.h. der String ist zwar nur 2 Zeichen lang, aber das darunterliegende Char-Array ist dasselbe wie vorher, und damit riesengross.

Ich verändere den Code geringfügig, konkret an der Stelle, an der ich der Variable "after" den String zuweise, der dann in die Liste eingefügt wird:

String after = new String(before.substring(before.length()-2));

Output nun:

0: before.length()=1000000; after.length()=2
1: before.length()=1000000; after.length()=2
2: before.length()=1000000; after.length()=2
3: before.length()=1000000; after.length()=2
[...]
5735: before.length()=1000000; after.length()=2
5736: before.length()=1000000; after.length()=2

... da habe ich Ctrl-C gedückt, es ward mir zu blöde, darauf zu warten, bis ich damit den Speicher voll kriege. Deutliche Garbage-Collection-Pausen haben sich jedoch gezeigt.

PH4Real
2007-02-28, 17:04:17
[viel interessanter Text über Strings]

Super Beitrag :up:! Wieder was gelernt und ich konnte es auch gleich anwenden. Bei meinen Projekt wurde nämlich so eine StringListe ziemlich groß (knapp 50MB), was ich aber weiter nicht beobachtet habe (RAM ist erstmal egal) mir aber schon komisch vorkam.

Nachdem ich das hier gelesen hatte, war mir klar warum :D.

Also... wo kann ich deinen Blog per RSS oder Podcasts, wo Du regelmäßig über solche Sachen schreibst bzw. darüber erzählst abonnieren ;).

Senior Sanchez
2007-02-28, 17:38:42
Super Beitrag :up:! Wieder was gelernt und ich konnte es auch gleich anwenden. Bei meinen Projekt wurde nämlich so eine StringListe ziemlich groß (knapp 50MB), was ich aber weiter nicht beobachtet habe (RAM ist erstmal egal) mir aber schon komisch vorkam.

Nachdem ich das hier gelesen hatte, war mir klar warum :D.

Also... wo kann ich deinen Blog per RSS oder Podcasts, wo Du regelmäßig über solche Sachen schreibst bzw. darüber erzählst abonnieren ;).

Dem kann ich nur zustimmen :)

Fand den Beitrag auch super.

Aber eine Frage hätte ich noch:

Wenn substring() quasi intern das char[] nur an den neuen String weiterreicht und ein neues Offset und eine neue Länge setzt, müsste doch ansich das char[] per Reference übergeben werden, sodass dieses doch gar keinen zusätzlichen Platz braucht, oder täusche ich mich gerade?
Insofern wäre doch der Speicherverbrauch gar nicht so exorbitant hoch, oder?

Aber laut der Exception wird wohl doch kopiert, nur warum?

Trap
2007-02-28, 18:00:13
Aber laut der Exception wird wohl doch kopiert, nur warum?
StringBuilder.toString() muss kopieren, der interne Speicher kann sich ja ändern und der String soll aber gleich bleiben.

Trap
2007-02-28, 18:00:48
doppelpost...

Senior Sanchez
2007-02-28, 18:03:16
Achso, stimmt ja.

PH4Real
2007-02-28, 18:10:54
StringBuilder.toString() muss kopieren, der interne Speicher kann sich ja ändern und der String soll aber gleich bleiben.

Ich glaube selbst beim StringBuilder.toString() kopiert er nicht... ich meine da gab es einen Flag, der bei jeder Aktion überprüft, ob der String geändert wurde oder so.

EDIT: Ah ok... seit 1.5 wird also nicht mehr das gleiche Array benutzt:

public String toString() {
// Create a copy, don't share the array
return new String(value, 0, count);
}


Und: http://radio.javaranch.com/weitzman/2005/08/03/1123093745052.html

Abnaxos
2007-03-01, 01:23:18
Ich glaube selbst beim StringBuilder.toString() kopiert er nicht... ich meine da gab es einen Flag, der bei jeder Aktion überprüft, ob der String geändert wurde oder so.
Es geht ja nicht darum, ob geändert *wurde*, sondern, ob geändert *wird*. Natürlich wird da kopiert, es muss: Ich kann jederzeit schreiben:

StringBuilder buf = new StringBuilder("abc");
String str = buf.toString();
buf.setCharAt(0, 'z');

=> Wenn da nicht kopiert wurde, habe ich in diesem Moment 'str' geändert, und das darf keinesfalls passieren, denn ein String ist per Definition unveränderbar.

EDIT: Ah ok... seit 1.5 wird also nicht mehr das gleiche Array benutzt:
Den StringBuilder gibt es überhaupt erst seit 1.5, vorher war der gar nicht da, da war es StringBuffer (ich schreibe es ja heute noch entsprechend: "StringBuilder buf = new StringBuilder()").

Das Problem an StringBuffer war, dass er thread-safe war. In 99.99999% der Fälle war das aber völlig unnötig. Man bedenke: Jedes "Hallo"+" "+"Welt" erzeugt temporär einen neuen StringBuffer und ruft darauf append() mehrfach auf -- thread-safe, also alles schön synchronisiert. Das ist völlig unnötig und unter dem Strich ein gewaltiger Overhead.

Daher wurde mit 1.5 StringBuffer durch StringBuilder ersetzt: Jetzt haben wir die Abwärts-Kompatibilität, indem StringBuffer noch immer thread-safe ist. Wir können (und sollen) jetzt aber auch StringBuilder verwenden, der Compiler tut das jetzt auch, wodurch der Overhead durch die Synchronisation eliminiert wird.

Abgesehen davon sind StringBuilder und StringBuffer identisch.

PH4Real
2007-03-01, 08:12:04
Es geht ja nicht darum, ob geändert *wurde*, sondern, ob geändert *wird*. Natürlich wird da kopiert, es muss: Ich kann jederzeit schreiben:

StringBuilder buf = new StringBuilder("abc");
String str = buf.toString();
buf.setCharAt(0, 'z');

=> Wenn da nicht kopiert wurde, habe ich in diesem Moment 'str' geändert, und das darf keinesfalls passieren, denn ein String ist per Definition unveränderbar.
[...]


Danke erst nochmal für die Ausführungen :). Dennoch bin ich mir ziemlich sicher, dass nicht kopiert wurde (vor 1.5 beim StringBuffer). Natürlich funktioniert dein Code auch schon vor 1.5 mit dem StringBuffer. Damit das so funktioniert hat, hatte die SUN Leute folgendes gebaut:


public String (StringBuffer buffer) {
synchronized(buffer) {
buffer.setShared();
this.value = buffer.getValue(); this.offset = 0;
this.count = buffer.length();
}
}


Dieser Konstruktor wird von StringBuffer.toString benutzt.

(Randbemerkung: synchronized(buffer) { ... }"lockt" das Objekt buffer für die Ausführung des Blocks gegen gleichzeitige Änderung durch ein anderes Task.)
buffer.getValue( ) besorgt von buffer die Referenz (!!!) auf dessen Zeichenpuffer. Sie wird auf this.value gespeichert. Es wird also kein neues Array angelegt, sondern das vorhandene des Objekts buffer "mitbenutzt".
Deshalb muß buffer.setShared( ) aufgerufen werden, damit buffer.shared auf true gesetzt wird.
Nun haben wir die Situation, daß ein String, dessen Inhalt per Definitionem von SUN unveränderbar ist, den Inhalt eines veränderlichen StringBuffer mitbenutzt. Aus diesem Grunde führt StringBuffer das Flag shared. Wenn ein "shared" StringBuffer verändert werden soll, verändert er nicht sein value-Array, sondern legt sich dann seinerseits eine Kopie an, vgl. oben StringBuffer.append und StringBuffer.copyWhenShared. Damit wird ein Kopieren in der häufigsten Anwendungssitutation des StringBuffer.toString ohne nachfolgende Veränderungen des StringBuffer vermieden.

siehe:
http://www.uni.koeln.de/rrzk/kurse/unterlagen/java/javaref/stringbuffer/implment.htm

Diesen Umstand hatte ich versucht zu beschreiben ;).

EDIT: Jaja... ich hatte oben StringBuilder und StringBuffer durcheinander gebracht, wie ich gerade sehe. Es ist mir jedoch schon klar, was beide für Aufgaben haben (hatten) bzw. dank deiner Ausführungen noch besser :).

ooAlbert
2007-03-01, 13:22:22
hi,

ich nochmal ...

ich hab mal versucht den vector gegen einen stringbuilder zu ersetzen, jedoch ist mir irgendwie nicht klargeworden wie die einzelnen elemente im stringbuilder organisiert sind. Im vector ist es ja wie in einem array, dh. letzes element ist ebend er letzte angefügte string...


so wars vorher :)

public String einlesen()
{
// Lies Textzeilen aus der Datei in einen Vector
try
{

URL url = new URL(getCodeBase(),"datei.txt");
// statt URL wenn Datei im Verzeichnis des Applets liegt

BufferedReader in = new BufferedReader(
new InputStreamReader(url.openStream()));

while ( null != (s = in.readLine()))
{

vec.add(s);
}
in.close();
fehler="weg";
}
catch (Exception ex)
{
//Fehlerbehandlung

fehler="da";
System.out.println("datei.txt defekt!");

}
if (fehler == "weg")
{
s = new String(vec.lastElement().toString());
vec.clear(); //leert Vector
}

System.out.println("Sytem aktiv");
return (s);
}



Jetzt ist es so:


public String einlesen()
{
// Lies Textzeilen aus der Datei in einen Vector
try
{

URL url = new URL(getCodeBase(),"datei.txt");
// statt URL wenn Datei im Verzeichnis des Applets liegt

BufferedReader in = new BufferedReader(
new InputStreamReader(url.openStream()));
StringBuilder sb = new StringBuilder();

while ( null != (s = in.readLine()))
{
sb.append(s);

}
in.close();
fehler="weg";
}
catch (Exception ex)
{
//Fehlerbehandlung

fehler="da";
System.out.println("datei.txt defekt!");

}
if (fehler == "weg")
{
s = sb.toString();
}
System.out.println("Sytem aktiv");
return (s);
}


Mein problem ist jetzt, wie bekomm ich das letze element aus dem Stringbuilder, dh. die letze eingelesene zeile aus der textdatei?? Denn wenn ich das richtig verstanden habe ist der stringbuilder einfach ein großer string ohne unterteilungen...

ich hab dann in der doku lastindexof gefunden, das brachte aber nicht den durchbruch:

String test = sb.substring(sb.lastIndexOf(".")+1);

Das fubnktioniert nur wenn ich ein gleichbleibendes symbol hätte, was ich aber nicht habe, das einzige was es gibt ist ein zeilen umbruch nach jeder zeile, nur sieht mand en ja nicht als symbol oder sowas ...

Übrigens würde meine konstruktion dann auch keinen overhead mehr produzieren oder immer noch nicht??
Warum wurde die classe vector eigentlich abgeschafft?

mfg

Gast
2007-03-01, 14:01:36
auf jeden fall ist fehler=="weg" unsinn, das ergibt immer false.

wenn du strings auf gleichheit überprüfen willst musst du equals verwenden bzw. equalsIgnoreCase wenn die groß/kleinschreibung egal ist.

abgesehen davon verwendet man natürlich nicht einen string zur fehlerüberprüfung.

Senior Sanchez
2007-03-01, 14:28:44
auf jeden fall ist fehler=="weg" unsinn, das ergibt immer false.

wenn du strings auf gleichheit überprüfen willst musst du equals verwenden bzw. equalsIgnoreCase wenn die groß/kleinschreibung egal ist.

abgesehen davon verwendet man natürlich nicht einen string zur fehlerüberprüfung.

Vorsicht ;)

Ist der String quasi zum Compile-Zeitpunkt schon bekannt (z.B. durch Konstanten), so funktionieren solche Vergleiche über ==.

ooAlbert
2007-03-01, 16:51:05
so mal unabhängig davon wie ich da nen fehler abfange ;)

nach einiger überlegung und vergleich der daten hab ich jetzt das:

int index = sb1.length();
String sub = sb1.substring( (index-104), index);

Damit bekomm ich meinen teilstring...
nun aber wiederum die frage hab ich jetzt den Overhead abgeschüttelt oder schleif ich den immernoch mit, wies Abnaxos anmerkte?

Gast
2007-03-01, 18:03:40
Ist der String quasi zum Compile-Zeitpunkt schon bekannt (z.B. durch Konstanten), so funktionieren solche Vergleiche über ==.

ok, mag sein dass es unter bestimmten umständen funktioniert, meistens ergibt es aber müll und mit equals ist man immer auf der sicheren seite.

Senior Sanchez
2007-03-01, 18:24:43
ok, mag sein dass es unter bestimmten umständen funktioniert, meistens ergibt es aber müll und mit equals ist man immer auf der sicheren seite.

Japs, das stimmt, nur ich wollte diesen Sonderfall mal erwähnt haben.

ooAlbert
2007-03-02, 07:50:32
Hi,

ich hab jetzt mal die ganze methode so angepasst das sie dem beispiel auf seite 1 in etwa ähnelt:


public String einlesen()
{
StringBuilder sb = new StringBuilder();

try
{

URL url = new URL(getCodeBase(),"datei.txt");
// statt URL wenn Datei im Verzeichnis des Applets liegt

BufferedReader in = new BufferedReader(
new InputStreamReader(url.openStream()));

while ( null != (s = in.readLine()))
{
sb.append(s);
}
in.close();
fehler="weg";
}
catch (Exception ex)
{
//Fehlerbehandlung

fehler="da";
System.out.println("datei.txt defekt!");

}
if (fehler == "weg")
{
int index = sb.length();
s = new String(sb.substring( (index-104), index));
}
System.out.println("Sytem aktiv");
return (s);
}


desweiteren interessiert mich immernoch:

warum man keine vektoren benutzen soll, bzw. wo deren nachteile liegen?
Ob jetzt der overhead etc. verschwunden ist und der GC den speicher und somit den heap selbstständig richtig ausleeren kann?

mfg

Monger
2007-03-02, 15:06:55
@ ooAlbert:
Du vermischst hier zwei völlig verschiedene Themen. Früher hat man StringBuffer dazu verwendet, um Stringfragmente zu einem großen String zusammenzubasteln. Den brauchst du also in aller Regel immer dann, wenn du dynamisch dir irgendwelche Strings erzeugen willst.

Wenn du

neuerString = "Hallo" + "Welt";

schreibst, lief da implizit dahinter ein StringBuffer ab, der aber halt nicht immer ganz so klever agieren konnte wie vielleicht nötig.

Ein Vector ist ein Container, um Daten in Form einer Liste zu strukturieren. Der ist dann auch dafür da, bestimmte Elemente per Index ansprechen zu können.

Für beide Elemente gibt es neue, unsynchronisierte Entsprechungen namens "StringBuilder" und "ArrayList".
Das findet man im gesamten JDK wieder: ursprünglich war Java extrem auf Parallelismus ausgelegt, und dementsprechend war man irrsinnig synchronisierungswütig. Jedes Element musste grundsätzlich mal dazu geeignet sein, in einer Multithreading Umgebung verwendet zu werden.

So Stück für Stück ist man da immer weiter zurückgerudert, weil erstens eine Synchronisierung ordentlich an der Rechenleistung zieht, und zweitens man sich damit immer wieder Deadlocks produzieren konnte die eigentlich völlig unnötig waren.

Deshalb: vergiss Vector und StringBuffer.

ooAlbert
2007-03-02, 15:37:48
aha, naja das leuchtet ein ... und das was ichd a oben gebastelt habe ist das jetzt speicherschonend? :)

Monger
2007-03-02, 16:49:34
aha, naja das leuchtet ein ... und das was ichd a oben gebastelt habe ist das jetzt speicherschonend? :)

Ich versteh ehrlich gesagt deinen Code nicht. Wozu dieses komische Fehler "Weg" und "Da"?
Und was genau soll

int index = sb.length();
s = new String(sb.substring( (index-104), index));

eigentlich machen? Ist 104 irgendeine magische Zahl? Und sehe ich das richtig, dass du "s" außerhalb der Methode deklariert hast?

Jetzt ohne Anspruch auf Richtigkeit vermute ich, dass du ungefähr sowas haben willst:


List<String> someStrings = new ArrayList<String>();
while(!(String s = sb.readLine())){
someStrings.add(s);
}

String lastStatement = someStrings.get(someStrings.index -1);
someStrings.remove(someStrings.index - 1);

return lastStatement;


Sämtliche String-Operationen - egal ob auseinanderschneiden oder zusammenflicken - sind immer speicher- und rechenintensiv. Wenn man die sowieso schon schön aufgetrennt nacheinander bekommt, wäre es doch blöd, die über einen StringBuilder zusammenzukleben, um sie danach mühsam wieder aufzutrennen. Lieber gleich alles in eine Liste reinpacken, und halt die Liste wegschmeißen wenn man sie nicht mehr braucht.
Deshalb fand ich deinen ersten Ansatz mit Vector eigentlich besser als deinen zweiten, nur "Vector" als Datenstruktur ist halt nicht so der Hit, und der Code könnte allgemein ein wenig aufgeräumter sein...

ooAlbert
2007-03-02, 19:16:24
also mal zum anfang... ich frug ob es sein kann, das ein vector sich im speicher ungewollt potenziert also overhead an die nachvolgenden variablen weitergiebt ... dann wurde mir ein beipsiel zu genau dieser problematik geschildert was beinhaltete, das vectoren sowieso out sind :) Also hab ich den ganzen kram umgebaut von vector auf stringbuilder... wie in dem beispiel. Es ist also eine adaption von mir dazu gewesen in der hoffnung das speicherlag loszuwerden.

"s" ist für die ganze Klasse definiert, weil das in anderen methoden noch gebraucht wird.

Das andere ist ein substring der von hinten ausgezählt abgeteilt wird, da es ja nun ein kompletter string ist und nicht wie im vector eine liste. Deshalb auch die frage nach der länge des stringbuilders. Somit ist der "index-104" genau die position von hinten ausgesehen wo meine letze ausgelesene zeile aus der textdatei beginnt.

Die variable fehler ist mal ganz nebensächlich die beeinflußt nur einige anzeigen die durch den fehler entstehen.


Also ich vermute das mit der arraylist ist das selbe wie ein vector nor besser ... weil nicht syncronisiert. Bevor ich das jetzt alles nochmal umschreibe :) kann ich davon ausgehen, das meine lösung und die gerade gepostete lösung mit arraylist keine speicherlags verusachen, dh. der GC den heap wieder freischaufelt etc. und ich somit bei gleichbleibender belastung von einem konstanten heapverbrauch ausgehen kann?

Denn der vector hatt mir den heap langfristig zugeknallt bis das applet halt abgestürzt ist.

Gast
2007-03-02, 19:17:10
Explizit auf null setzen ist aber nicht nötig, wenn du den Sichtbarkeitsbereich eh verläßt. Vorsicht mit solchen Patterns wie Singletons... mit denen kann man sich fürchterliche Speicherlöcher bauen (ich ärger mich immer noch :biggrin:).hm, das kann ich mir jetzt gar nicht vorstellen... kannst du dafür mal ein Beispiel bringen?
Oder habe ich vielleicht den Begriff "Speicherloch" falsch verstanden? Spontan würde ich damit nämlich verbinden, daß der GC irgendwann mal das Singleton zerstört, du aber danach aber noch darauf zuzugreifen versuchst, also eine NullPointerException. Meinst du vielleicht das Gegenteil, eine Speicherleiche, die nicht freigegeben wird, obwohl du sie gar nicht mehr brauchst? Und die im Falle eines Singleton deswegen nicht freigegeben wird, weil in irgendwelchen hintersten Winkeln deines Programmes noch Referenzen darauf vorhanden sind, auch wenn sie gar nicht mehr benutzt werden?

Monger
2007-03-02, 21:21:48
"s" ist für die ganze Klasse definiert, weil das in anderen methoden noch gebraucht wird.

Wozu brauchst du eine einzelne Zeile, wenn du diese auch gleichzeitig als Rückgabeparameter hast?
Ich befürchte halt, dass du dir da irgendein Konstrukt gebaut hast, was noch alle Referenzen von früheren Berechnungen hält. Von wievielen String Objekten reden wir hier eigentlich? Denn so schnell ballerst du dir normalerweise mit nix den Heap voll.


Also ich vermute das mit der arraylist ist das selbe wie ein vector nor besser ... weil nicht syncronisiert. Bevor ich das jetzt alles nochmal umschreibe :) kann ich davon ausgehen, das meine lösung und die gerade gepostete lösung mit arraylist keine speicherlags verusachen, dh. der GC den heap wieder freischaufelt etc. und ich somit bei gleichbleibender belastung von einem konstanten heapverbrauch ausgehen kann?

Denn der vector hatt mir den heap langfristig zugeknallt bis das applet halt abgestürzt ist.
Du kannst davon ausgehen, dass das JDK bugfrei ist! ;)
Wenn du einen Heap Overflow kriegst, ist das fast immer ein sicheres Zeichen dafür, dass du zu viel Daten permanent hältst.

Versuch den Scope jeder Variablen auf ein Minimum zu begrenzen, leg nur da Attribute an wo sie wirklich nötig sind, vermeide doppelt verkettete Datenstrukturen (z.B. ein Objekt, dass sowohl in irgendeiner Map als auch in irgendeiner Liste drin hängt), und dann sollte alles funktionieren. Du baust dir hier ja nix exotisches zusammen, sondern einen hundsgewöhnlichen Textreader.


Nachtrag: Argh, ich bin so doof!
Ist doch ganz klar: wenn du dein "s" auch außerhalb der Methode hältst und deine Liste nie leerst, fügst du bei jedem Methodenaufruf wieder die gleiche Menge an Objekten hinzu. Dann ist es doch kein Wunder, dass es nach ein paar malen knallt! ;)

Köppchen
2007-03-03, 00:11:03
@ooAlbert
das erste Beispiel sieht so aus als wolltest du nur die letzte Zeile der Datei. Warum speicherst du dann die anderen Zeilen zwischen?
Und ist die letzte Zeile wirklich immer gleich lang (aus deinem substring... geschlossen)? Dann könntest du mit skip(long n) sogar direkt die uninteressanten Bytes überspringen.

Gruß Markus

ooAlbert
2007-03-03, 16:09:44
ja die zeilen sind gleich lang und durch einen zeilenumbruch abgetrennt in der textdatei.

ja ich will immer nur die letzte zeile, da ich keine zeilen nummern habe oder sowas und die datei dynamisch ist also mal kürzer mal länger sein kann hab ich halt gedacht lies alles aus und jede zeile ist ein teil vom vector, da wärs sogar egal ob die gleichlang sind :)

Wegen dem s, muß ich nochmal schaun warum ich das überhaupt als Rückgabewert habe wenns doch eh globalisiert ist.

An sich sollte es so funktionieren die erste methode ließt die datein aus und brüft schonmal ab ob die datei überhaupt da ist und ob überhaupt was drin steht.
die letze zeile sollte dann in das "s".
Danach soll in der zweiten Methode das "s" weiter aufgespaltet werden um so die eigentlichen werte aus der Textzeile zu gewinnen, also wieder substring und dann die substrings in integer oder float werte wandeln.

Die dritte Methode macht die eigentliche berechnung und die weiteren sind mit paint() verknüpft was die werte dann grafisch ausgiebt.


Außerdem dachte ich vorher, der vector wurde alle 5min erneut bestückt durch ein auslesen der aktuellen Textdatei. deshalb meinte ich, das der vector jedesmal überschreiben wird also garnicht aufblähen kann. Dann dachte ich der GC schmeißt den vector eh innerhalb der 5min mal raus, da der ja nirgend mehr gebraucht wird. Genauso dachte ich das von "s" das wird auch nur alle 5min erstellt.
Kann es eigentlich sein, das klassenglobale variablen vom GC seltener "rausgeschmissen" werden als Methodenglobale?



Nachtrag: Argh, ich bin so doof!
Ist doch ganz klar: wenn du dein "s" auch außerhalb der Methode hältst und deine Liste nie leerst, fügst du bei jedem Methodenaufruf wieder die gleiche Menge an Objekten hinzu. Dann ist es doch kein Wunder, dass es nach ein paar malen knallt! ;)

Aber wie schon erwähnt "s" wird doch jedesmal überschreiben beim neuauslesen ... zumindestens dachte ich das. Mal angenommen ich würde das vorher mit einem befehl mit NULL füllen, dann wäre es doch leer und wäre bei jedem auslesen wieder junfräulich oder ist das ein irrglaube? :)

Monger
2007-03-03, 16:59:21
Wegen dem s, muß ich nochmal schaun warum ich das überhaupt als Rückgabewert habe wenns doch eh globalisiert ist.

Ich kenn nunmal das restliche Programm nicht, aber: du solltest keine Attribute verwenden, wo es ein Rückgabeparameter auch tut. Das ist auch designtechnisch sinnvoll: die Signatur einer Methode sollte eigentlich vollständig erklären, was sie tut. Wenn jetzt z.B. jemand von deiner Klasse erbt und an dem Attribut rumfummelt, funktioniert plötzlich deine Methode nicht mehr ordnungsgemäß, und es ist für niemanden einsichtig, warum.
Es gibt natürlich Situationen wo das Sinn macht bzw. auch absolut notwendig ist, aber alleine dass du dein Attribut "s" nennst, sagt mir dass es keine wirklich große Bedeutung für die restliche Klasse haben kann.

Danach soll in der zweiten Methode das "s" weiter aufgespaltet werden um so die eigentlichen werte aus der Textzeile zu gewinnen, also wieder substring und dann die substrings in integer oder float werte wandeln.

Schau dir mal dazu die "String.split()" Methode an. Vorher hast du da irgendwas von index und substring geredet - mit split geht es deutlich simpler.


Kann es eigentlich sein, das klassenglobale variablen vom GC seltener "rausgeschmissen" werden als Methodenglobale?

Was sind denn "Methodenglobale"?
Wie auch immer: Variaben leben solange wie die Methode, in der sie deklariert sind. Attribute leben solange wie das Objekt lebt. In aller Regel ist letzteres natürlich BEDEUTEND länger als ersteres.



Aber wie schon erwähnt "s" wird doch jedesmal überschreiben beim neuauslesen ... zumindestens dachte ich das. Mal angenommen ich würde das vorher mit einem befehl mit NULL füllen, dann wäre es doch leer und wäre bei jedem auslesen wieder junfräulich oder ist das ein irrglaube? :)
Wenn du

s = sb.readLine(...);

schreibst, wird s eine neue Referenz zugewiesen. Vorher explizit null setzen bringt dir da keinen Vorteil. Das alte Objekt ist aber selbstverständlich noch da. Wenn da irgendwelche Referenzen noch von außen darauf existieren, lebt es natürlich weiter.

Übrigens: wenn du wirklich nur die letzte Zeile brauchst, geht's noch wesentlich einfacher! ;)

Wie wär's damit:

String lastLine;
while(String s = sb.readLine(in){
lastLine = s;
}
return lastLine;

Ich bin mir nicht ganz sicher, ob die Schleife auch noch die Zuweisung macht wenn aus "readLine" ein false rauskommt, deshalb der Umweg über die "lastLine". Wahrscheinlich geht's aber sogar noch simpler.

Köppchen
2007-03-04, 00:03:46
@ooAlbert, so ähnlich hatte ich das angenommen (Aus deinen 2 Codebeispielen geschlossen).
Du kannst das so machen wie Monger geschrieben hat, das vermeidet wenigstens unnötige festhalten der kompletten Datei im Speicher.
Wie aber schon geschrieben kannst du mit skip(...) uninteressante Zeichen der Datei überspringen. Mit length() bekommst übrigens die Gesamtgröße der Datei.

Gruß Markus