Du verwendest einen veralteten Browser. Es ist möglich, dass diese oder andere Websites nicht korrekt angezeigt werden. Du solltest ein Upgrade durchführen oder ein alternativer Browser verwenden.
Ich habe gelesen, dass in Java der Garbage Collector dafür sorgt, dass nicht mehr verwendete Objekte eingesammelt und entsorgt werden.
Ich frage mich was eine gute Praxis ist.
Kann man einfach auf den Garbage Collector vertrauen - der wird's schon richten - oder sollte man, nach Verwendung, die Objekte manuell freigeben (wie man es vielleicht aus anderen Sprachen sich angewöhnt hat) ?
Nein, brauchst du nicht, und geht in Java auch gar nicht. Einfach nichts tun mit den Objekten.
Es sei denn natürlich, dass du als Teil deines Objektes auch andere Systemressourcen mit Plattform abhängigen APIs alloziierst, die nicht durch die JVM gemanaged werden, und die du auch wieder explizit freigeben musst. Das ist aber eher selten der Fall.
Zusatzfrage: wann kommt der Garbage Collector vorbei? Während der Ausführung oder am Schluss?
Ich habe grössere Berechnungen und bin möglicherweise froh, wenn der Speicher unterwegs schon freigegeben wird.
Du kannst i.d.R. auf den GC vertrauen. Zu beachten ist aber, dass native Ressourcen u.U. geschlossen werden müssen. Also z.B.: alles was mit JDBC oder Streams/Filesystem zu tun hat. Es gibt aber trotzdem die Möglichkeit sich selbst eine Falle zu stellen. Wenn es dazu kommst, dass Referenzen auf erzeugte Objekte nie aufgelöst werden, Dann kann der GC nicht erkennen das diese Objekte beseitigt werden können. Das führt zu s.g. Speicherlecks und in der Folge zum Absturz deines Programms. Als Anfänger solltest du dir aber deswegen nicht das Hirn zermartern sondern einfach sauber programmieren lernen.
Nein, da ist nichts notwendig. So eine Optimierung wäre evtl. denkbar, wenn Du eine große Instanz hast (z.B. große Bitmap) und es sich um eine Instanzvariable handelt die nicht neu überschrieben wird oder so ... Aber sowas habe ich irgendwie noch nie gehabt und es ist auch normalerweise nicht notwendig.
Zusatzfrage: wann kommt der Garbage Collector vorbei? Während der Ausführung oder am Schluss?
Ich habe grössere Berechnungen und bin möglicherweise froh, wenn der Speicher unterwegs schon freigegeben wird.
Das ist Abhängig von der genauen Implementation. Es gibt diverse GC Implementationen.
In dem "Normalfall" läuft der GC in einem eigenen Thread nebenher und wird von Zeit zu Zeit aktiv. Des Weiteren kann er an getriggert werden, wenn Speicher reserviert werden soll aber derzeit nicht genug freier Speicher zur Verfügung steht.
Zusatz-(und letzte) Frage zum Thema Speicher:
Beide folgende Varianten funktionnieren. Ich habe aber das Gefühl, die erste Variante ist speichertechnisch verschwenderisch. Stimmt das so ? Sprichst sonst etwas für die eine oder für die andere Variante.
Java:
while (...) {
double zwischenResultat = ...;
...
}
Java:
double zwischenResultat;
while (...) {
zwischenResultat = ...;
...
}
Es kommt hier auf die ganz konkrete JVM Implementierung an. Und in einer "tiered compilation" JVM wie HotSpot, kommt es auch noch darauf an, mit welchem Compiler der Code dieser Methode kompiliert wurde. Und das wieder hängr davon ab, wie gross die Methode ist und wie oft sie ausgeführt wurde. Sagen wir mal, dass alle Bedingungen erfüllt sind und mit C2 compiliert wird. Dann findet sowieso eine Liveness und Escapeanalyse statt und die Variable wird sofort abgeräumt nach der Methode, wenn keine Instanz die Methode (bzw. ganz korrekt den "Inline-Boundary") verlässt.
Grundsätzlich ist das eine eher akademische Diskussion - interessant, aber nicht praxisrelevant. Denn das was man optimieren könnte ist nicht messbar. Wichtiger ist es sauberen Code zu schreiben - und da ist die erste Variante in der Regel besser als die erste, weil der Scope der Variablen damit kleiner und klarer ist.
Ok verstehe ich.
Ich mache gerade sehr grosse Berechnungen mit Millionen von Iterationen und da fragte ich mich eben, ob es nicht doch auch relevant sein könnte.
Wenn man in Java Probleme mit dem Speicher hat, dann sind es nicht einzelne Instanzen, sondern Referenzen die man auf Listen nicht mehr benötigter Daten (wie z.B. Bilder) hält.
Optimierungsmöglichkeiten gibt es da bestimmt - einen double vor oder in der Schleife zu deklarieren macht da aber keinen Unterschied. Kleinerer Scope ist grundsätzlich schon sinnvoll, allein schon zum vermeiden von Fehlern, und könnte u.U. auch dem Compiler bei Escape Analysis helfen – Ausnahmen gibt es aber auch dort, zb wenn man damit deutlich weniger Objekte alloziieren muss, und der Compiler das nicht selbst optimieren kann.
Je nach Art der Berechnungen macht vielleicht auch Nutzung der Vector API Sinn.
Aber es stimmt schon: In Hochsprachen zu optimieren ist grober Käse, da kommst du nicht weit. Ein neues Javaupdate, ein anderer Compiler - und schon verhält sich dein Programm wieder etwas anders bezüglich der Laufzeit und Speicherverhalten. Und dann kommt noch dazu, daß in Java ja noch zur Laufzeit etwas umherkompiliert wird. Das genaue Laufzeitverhalten ist da praktisch unvorhersehbar, wenn man nicht gerade direkt an der JMV-Entwicklung beteiligt ist (und womöglich selbst dann nicht einmal).
Bei hardwarenahen Sprachen wie C wird das zwar oft gemacht, aber da hat man eigentlich genau dieselben Probleme: Anderer Compiler, andere Optimierungsoptionen - und schon läuft das Programm etwas anders als gewollt/gedacht.
Daher: Wenn man ernsthaft Ressourcen optimieren will/muß, macht man das in Assembler - oder gar nicht. Hochsprachen sind mal dafür entwickelt worden, um von der konkreten Maschine zu abstrahieren, und daher ist es schon rein vom Ansatz her völlig unsinnig, in Hochsprachen irgendwas optimieren zu wollen. Und wenn du dich in diese Gegend einarbeiten willst würde ich dir raten, tatsächlich mal ASM zu programmieren. Kleine 8-Bit-Mikrocontroller oder so etwas...da lernt man sehr viel und man sieht dann auch, wieviele Möglichkeiten man auf der Maschine hat, einen Haufen Anweisungen abzuarbeiten. Ich greife dein Beispiel mal auf:
Zusatz-(und letzte) Frage zum Thema Speicher:
Beide folgende Varianten funktionnieren. Ich habe aber das Gefühl, die erste Variante ist speichertechnisch verschwenderisch. Stimmt das so ? Sprichst sonst etwas für die eine oder für die andere Variante.
Java:
while (...) {
double zwischenResultat = ...;
...
}
Java:
double zwischenResultat;
while (...) {
zwischenResultat = ...;
...
}
Es ist keineswegs gesagt, daß der Computer diese Schleife tatsächlich als Schleife ausführt. 'Schleife' heißt, am Ende einen Rücksprung zum Startpunkt der Schleife zu haben - und Sprungbefehle kosten Rechenzeit. Einmal zum Zurückspringen, und dann zum Prüfen der Abbruchbedingung der Schleife.
Jetzt gibt es aber nichts, was den Compiler z.B. daran hindert, die Anweisungen in der Schleife einfach dreimal hintereinander zu kopieren - ohne Sprungbefehle. Wenn der Speicher egal ist, aber ein paar Takte gespart werden können - warum nicht?
Nicht selten weiß man als Programmierer ja auch nicht, wie oft die Schleife ausgeführt wird. Aber die JVM hat vielleicht schon eine Ahnung, und macht zur Laufzeit, was sie gerade für richtig hält - je nach JVM-Implementierung, Rechner, Mondphase und ob die JVM um Mitternacht von 12 Jungfrauen kompiliert wurde oder nicht.
Es wäre auch durchaus denkbar, daß deine Variable 'zwischenResultat' nur in einem Rechenregister existiert. Da muß sie sowieso rein, um eben darauf zu rechnen. Dann würde dafür aber überhaupt kein Speicher alloziiert werden, und jede Diskussion über den Speicherbedarf wäre völlig gegenstandslos.
Aber das sind alles Dinge, die weißt du zur Programmierzeit einfach nicht. Sie sind sogar so gebaut, daß du sie nicht wissen mußt. Also vertrau den Machern der JVM und dem ganzen Drumherum - einerseits sind das kluge Leute, andererseits hast du sowieso keine andere Wahl (sofern du nicht JVM-Assembler lernen willst).
Numerik ist eine Wissenschaft für sich. Ein Kollege auf Arbeit befaßt sich gerade etwas mit der Berechnung von elektrischen Feldern in Python. (Und ich selber habe auch etwas ähnliches vor, wenn mein aktuelles Projekt ausgereift ist.)
Der erzählte mir neulich mal, daß es einen großen Unterschied macht wie man berechnet. Man nehme z.B. ein sehr großes Array von Fließkommazahlen und bilde die Summe. Wenn man einfach über das Array iteriert und stumpf alles in einer Schleife aufsummiert läuft man irgendwann in das Problem, daß die Addition von sehr großen und sehr kleinen Werten nach und nach zu Fehlern führt.
Dann gibt es Verfahren, daß man z.B. erst ähnlich große Zahlen addiert, und dann die Zwischensummen davon, usw.
Das sind Dinge, die würde ich mir an deiner Stelle mal ansehen, denn die prinzipiellen Unzulänglichkeiten moderner Rechentechnik, wie sie z.B. durch die Darstellung von Fließkommazahlen zustangekommen, kann dir die JVM auch nicht wegoptimieren. Ich muß jedoch noch hinzufügen daß ich mich damit selber noch nicht beschäftigt habe und das nur vom Hörensagen weiß.
Da hast du bzw. dein Kollege absolut recht. Ich hantiere gerade mit sehr grossen Zahlen herum, und da liegt tatsächlich viel Optimierungspotential in der Organisation der Berechnungen! Anspruchsvoll aber sehr interessant, kann ich nur empfehlen ;-)