hallo
der heap ist jener speicherbereich, in dem die objekte liegen. wenn ein objekt felder hat, dann benötigten diese felder ja speicherplatz. heap (deutsch: haufen) ist einfach ein grosser speicherbereich, in dem das programm relativ frei speicher anfordern kann.
man muss dabei immer unterscheiden: lokale variablen einer methode liegen immer am stack (ein anderer speicherort, der allerdings weitaus weniger flexibel bei der speicherverwaltung ist, dafür automaitsch bereinigt wird, wenn die methode beendet wird). alle felder eines objekts liegen immer am heap. aufpassen bei lokalen objektvariablen: hier liegt nur die objektreferenz (4 byte) am stack, das objekt selbst liegt immer am heap.
ist der stack zu klein, bekommst du einen stack-overflow. ist leicht zu diagnostizieren. auch hast du keine performanceprobleme bis dahin. in deinem fall also völlig auszuschließen. abgesehen davon, dass der stack bei java standardmässig 512 K gross ist, was ganz schön gross ist.
die grösse des heaps kannst du zur laufzeit prüfen:
Runtime.getRuntime().totalMemory() liefert die grösse des heaps in bytes als long-wert.
Runtime.getRuntime().freeMemory() liefert die grösse des freien speichers ebenfalls als long-wert.
total - free ist logischerweise der used-wert.
total kann sich zur laufzeit ändern! je nach startparameter fängt die vm meist mit einem eher kleine heap an und vergrössert den dann bei bedarf. teilweise wird er auch wieder verkleinert, wenn er nicht gebraucht wird. kann man alles über vm-parameter einstellen.
wichtig dabei noch: der heap ist nicht ein grosser block sondern in mehrere segmente aufgeteilt. es gibt einen bereich, in dem neue objekte angelegt werden (eden space), der darauf optimiert ist, die objekte auch recht schnell wieder zu entfernen (in den meisten programmen werden die meisten objekte nur kurz erzeugt und gleich wieder weggeschmissen). dann gibts einen bereich für langlebigere objekte und auch einen bereich für den klassenspeicher, wo die klassen hingeladen werden.
üblicherweise ist der eden space für objektinstanzierung reserviert und kann nicht frei genutzt werden. je nach vm und vm-parameter ist die grösse dieses bereichs unterschiedlich, du musst aber mit ca. 10 % rechnen.
der eden space ist zusätzlich noch zweigeteilt. das hängt mit der strategie des garbage collectors zusammen:
neue objekte werden in der ersten hälte des eden space angelegt.
ist diese hälfte voll, werden alle objekte in die zweite hälfte umkopiert. dabei prüft der garbage collector allerdings, ob diese objekte überhaupt noch referenzen haben. wenn nicht, wird eben nicht kopiert. aus der erfahrung weiss man, dass die meisten objekte nur ganz kurz benötigt werden, daher ist das eine ziemlich gute strategie.
objekte, die öfter umkopiert werden (anzahl pro vm wieder unterschiedlich) sind offenbar langlebiger und werden daher in den langzeitspeicher kopiert.
solange der langzeitspeicher gross genug ist, wird nur sporadisch und nebenbei im langzeitspeicher auf nicht mehr referenzierte objekte geprüft. dies sollte auch der „normale“ zustand eines programms sein und ist normalerweise ausreichend.
geht der langzeitspeicher zur neige, wird eine intensivere suche nach nicht referenzierten objekte durchgeführt. dabei kann es schon zu performanceeinbrüchen kommen. meist fällt das aber nur auf, wenn gerade eine wirklich cpu-intensive aktion erfolgt.
wird auch bei der etwas intensiveren suche nicht genug freigeräumt, muss die vm einen full collect starten. dabei werden alle threads ANGEHALTEN(!). danach wird der gesammte speicher komplett nach nicht referenzierten objekten durchsucht und diese freigegeben. bei einem grossen heap kann das schon mal mehrere sekunden dauern, d.h. das java-programm steht zu dieser zeit komplett und läuft dann erst weiter.
hilft auch das alles nichts, macht die vm die radikalkur: es werden auch die klassen aus dem klassenspeicher entladen, in der hoffnung, dass inzwischen einige klassen nicht mehr benötigt werden (z.b. wenn sie nur für die initialisierung des programms, nicht aber für den normalbetrieb notwendig waren). ist auch dort sinnvoll, wo viele dynamische klassen generiert werden. diese klassen müssen dann natürlich bei bedarf neu geladen werden, was auch die performance ordentlich drückt.
soweit zum hintergrund…
du kannst nun einen hintergrundthread schreiben, der permanent die grösse des heaps sowie den freien speicher ausgibt. bei der grafischen auswertung (z.b. mit excel, wenn man sonst nichts hat) sollte üblicherweise eine art sägezahnmuster entstehen: der heap wird nach und nach gefüllt und dann wieder geleert. das ist das normalverhalten. augenmerk muss man auf die „talsohle“ legen, also die maximale menge an freien speicher, den man hat. nach einer gewissen startphase, die von programm zu programm verschieden ist, sollte sich der speicherbereich einpendeln, d.h. die max. menge an freien speicher sollte in etwa gleich bleiben.
wenn die menge an freien speicher über die zeit hinweg aber permanent abnimmt (kann über längeren zeitraum sein), dann hat man ein problem. das programm läuft solange stabil, solange ausreichend speicher freigeschaufelt werden kann. ist zuwenig platz für den eden space da, kommt der full-collect, der die performance niederreisst.
grund für die permanente abnahme des speichers kann ein memory leak sein. viele programmierer vergessen gerne, alle referenzen wirklich freizugeben. meist sind es objektrefernezen in irgendwelchen collections oder registrierte listener (klassisch: ein fenster registriert sich bei einem anderen als listener. dann wird das fenster geschlossen. es ist nun zwar unsichtbar, da es aber immer noch eine referenz darauf gibt, kann es nicht wirklich gelöscht werden. das remove-listener wird ganz gerne vergessen).
oder aber es ist einfach in der natur der sache, dass das programm ständig speicher braucht. da brauchst du entweder eine komplett andere programmierstrategie, was manchmal ein komplettes redesign und damit neuprogrammieren bedeutet. oder eben mehr speicher.
unter einer 32-bit-java-vm ist aber bei ca. 2 gb schluss (abängig von vm, betriebssystem usw.).
die grösse des heaps steuert man über startparameter der vm, was aber bei applets nicht so leicht ist - könnte den anwender etwas überfordern. seit java 5 kann man den heap auch zur laufzeit regeln, was aber etwas erfahrung mit der management konsole voraussetzt.
bevor du aber jetzt die vielen einstellungsmöglichkeiten für den heap erfragst, solltest du erst prüfen, ob es tatsächlich mit dem heap zusammenhängt. also einfach einen kleinen hintergrundthread, der die werte in einer datei protokolliert.
oder du findest eine möglichkeit, dich mit der management konsole an die vm des applets anzuhängen. da ich aber mit applets wenig arbeite (sind inzwischen ja doch schon grob aus der mode gekommen), kann ich da wenig weiterhelfen.
lg
erwin