Hallo Timo,
die Implementierung von String und StringBuffer ist eine unterschiedliche.
Strings sind "immutable", d.h. durch hinzufügen von neuen Zeichen zu einem String bekommst Du immer eine neue Instanz. Deine Varable (nennen wird Sie mal s) zeigt somit immer auf eine neue Instanz eines String-Objekts. Der alte String ist aber immer noch im Speicher und wird vom GC entsorgt.
Wenn jetzt der Speicher knapp wird, so schlägt der GC immer öfter zu und versucht freien Speicher zu finden. Wenn jetzt durch einen new Aufruf (indirekt wird dies bei Hinzufügen von Zeichen zu einem String gemacht) kein freier Speicher mehr gefunden werden kann, dann wirft die VM eben einen OutOfMemoryError, d.h. es kann kein Speicher mehr freigegeben werden, da der "Pool" für Speicher leer ist (du hast ja den ganzen Speicher belegt).
Das ist so ähnlich, als könntes Du nur so viel Speicher allozieren, wie Du physikalisch in Deinem Rechner hast. Der Speicher ist i.d.R. in der VM nicht virtualisisert.
Wie schon meine Vorredner gesagt haben, ist dieser "Pool an Speicher" also der Heap von VM-Implementierung zu VM-Implementierung verschieden. Jedoch kann durch Parameter beim Starten der VM diesen Heap größer (oder auch kleiner) machen.
Bei dem StringBuffer wird i.d.R. keine neue Instanz des StringBuffer-Objekts erzeugt, aber intern ist trotzdem ein Array in dem die Zeichen gespeichert sind. Dieses muß natürlich angepasst werden, wenn das Array voll ist. Nun stell Dir vor, Dein StringBuffer Objekt hält n Zeichen (und das interne Array ist voll). Wenn nun ein Zeichen hinzukommt, so wird ein neues Array mit n+e Zeichen (mit e eine Konstante; siehe StringBuffer Konstruktor) erzeugt und der Inhalt aus dem alten Array in das neue Array kopiert. Du brauchst also Minimum 2n+e Zeichen-Speicher. Wenn Du aber nur m Zeichen-Speicher hast und m < 2n+e ist, hast Du eben obigen OutOfMemoryError.
BTW. Du sprichts von Byte für den Speicherverbrauch. Strings werden aber in UTF-8 abgelegt und da kann ein Zeichen unter Umständen schon mal 3 Bytes lang sein. Wenn Du Dich aber auf ASCII beschränkst, kannst Du sizeof(char)=sizeof(byte) annehmen (was man aber eigentlich nicht sollt ;-)
hab hier eine aufgabe, bei der ich herausfinden soll, wie
gross ich einen String (bzw. StringBuffer) machen kann, befor
es $subject Fehler gibt.
Hier ist das Wörtchen bevor wohl wichtig. Dann versuch doch eine while-Schleife zu machen, in der Du ein String/StringBuffer Objekt immer n=1024 Zeichen hinzufügst. Das Hinzufügen sicherst Du mit einem try catch-Block ab. Im catch Block fängst Du die Exception ab, setzt den Inhalt Deines neuen Strings/StringBuffers auf null, halbierst Zeichenanzahl n, und nimmst für die nächste Runde den alten Wert. Im catch-Block kannst Du noch System.gc() und Thread.yield() aufrufen um den GC explizit aufzurufen. Wichtig ist nur, dass Du keinen Speicher im catch Block allozierst.
Mit dieser Methode allozierst Du (bis auf ein Epsilon) den kompletten Speicher.
Wenn Du n immer verdoppelst, dann ist 2^25 die Zahl die fast der Hälfte des zur Verfügung stehenden Speichers entspricht (Argumentation siehe oben mit 2n+e) (das ist aber jetzt aus dem Bauch geschossen, ohne diese Behauptung bewiesen zu haben; außerdem hängt es von der Implementierung von String.concate bzw. StringBuffer ... ab. Soweit ich weiß, gibt es keine effiziente Methode ein Array um n Einträge zu erweitern. Es muß dazu immer ein neues Array mit (altes Array).length+n Einträgen alloziert werden.
Ich hoffe, daß ich nicht zu langweilig war.
Gruß,
Frank