Speichert eine statische Methode Speicher für eine Klasse, von der Sie viele Instanzen haben?

8

Als Antwort auf Aaronaught's Antwort auf die Frage unter:

Kann ich nicht einfach alle statischen Methoden verwenden?

Wird für eine statische Methode nicht weniger Speicher verwendet? Ich habe den Eindruck, dass jede Objektinstanz ihre eigene ausführbare Version einer nicht statischen Elementfunktion enthält.

Unabhängig davon, wie viel Overhead mit dem Aufrufen einer statischen Methode verbunden ist, unabhängig vom schlechten OO-Design und möglichen Kopfschmerzen, verbraucht sie zur Laufzeit nicht weniger Speicher?

Hier ist ein Beispiel:

Ich mache einen Vektor aus nullinitialisierten Objekten. Jedes Objekt enthält ein Datenelement (ein Dreieck aus neun Doppeln). Jedes Objekt wird nacheinander aus Daten gefüllt, die aus einer STL-Datei gelesen wurden. Es wird nur eine statische Methode benötigt. Das richtige OO-Design schreibt vor, dass eine Methode, die sich direkt mit den Daten befasst, auf jedes Objekt verteilt werden soll. Hier ist die Standard-OO-Lösung:

foreach(obj in vec) {
  obj.readFromFile(fileName);
}

Jedes Objekt trägt readFromFileden kompilierten Code neben den Daten!

Der Speicher ist in diesem Fall eher ein Problem als die Leistung, und auf einem eingeschränkten System befinden sich viele Daten.

Lösungen:

  1. Namespace-Methode (ideal für C ++, aber in Java nicht möglich)
  2. Eine statische Methode in der Klasse von obj. Ausführbarer Code wird zur Laufzeit an einem Ort gespeichert. Das Aufrufen der Methode ist mit einem geringen Aufwand verbunden.
  3. Eine übergeordnete Klasse, von der obj abgeleitet ist und die die private Methode enthält readFromFile. Anruf mit super.callPrivateMethod()welchen Anrufen readFromFile. Chaotisch und immer noch etwas Speicheraufwand in jedem Objekt.
  4. Implementieren Sie readFromFileaußerhalb des Bereichs von obj, also in der vec-Klasse oder in der aufrufenden Klasse. Dies unterbricht meiner Meinung nach die Datenkapselung.

Mir ist klar, dass für große Datenmengen ein explizites Objekt für jedes Dreieck nicht der beste Ansatz ist. Dies ist nur ein Beispiel.

Panlex
quelle
7
In welcher Sprache denken Sie, dass der Code für jede Instanz dupliziert wird? Dies ist in Java, C #, C ++ nicht der Fall.
Joshp
2
Nun, es könnte mit einer dynamischen Sprache in den Kinderschuhen oder als Nebeneffekt einer missbrauchten Reflexion geschehen. Aber im Allgemeinen, nein, ein Byte wird nicht viel größer sein als das darin enthaltene Byte.
Matthew Mark Miller
1
Die einzige Sprache, von der ich weiß, wo Sie dieses speicherverschwendende Verhalten erhalten können, ist Javascript, aber selbst dann müssten Sie bei prototypal OO völlig versagen, um dies zu tun. In JS sollten Sie (sowohl statische als auch nicht statische) Methoden für das Prototypobjekt festlegen, damit alle Objekte, die von diesem Prototyp erben, diese eine Funktion ohne Duplizierung gemeinsam nutzen.
Ixrec
2
Haben Sie darüber nachgedacht, ein kleines Programm zu schreiben, das Tausende solcher Objekte erstellt und die Messungen selbst durchführt?
Bryan Oakley
1
Nicht die Antwort auf jede OO-bezogene Frage: Abhängigkeitsinjektion? Wenn Sie sich über den Speicheraufwand Gedanken machen, können Sie einfach ein "Sparer" -Objekt entwerfen, von dem Sie eine Instanz erstellen, die injiziert werden kann ...
Pieter B

Antworten:

19

Methoden werden auch bei virtuellen Methoden nicht auf Instanzbasis gespeichert. Sie werden an einem einzigen Speicherort gespeichert und "wissen" nur, zu welchem ​​Objekt sie gehören, da der thisZeiger beim Aufrufen übergeben wird.

Der einzige zusätzliche Speicher, der in C ++ benötigt wird, ist, wenn Sie virtuelle Methoden verwenden, für die ein einzelner zusätzlicher Zeiger pro Instanz erforderlich ist, um auf die virtuelle Methodentabelle zu verweisen (in Java haben Sie natürlich immer eine Basisklasse mit virtuellen Methoden Object, daher ist dies unvermeidlich ).

Wenn es so funktionieren würde, wie Sie es beschrieben haben, wären OO-Sprachen nicht sehr beliebt! Sie können Ihren Objekten beliebig viele Methoden hinzufügen. Dies hat keinen Einfluss auf die Speichernutzung.

Vrostu
quelle
Es gibt auch einen Overhead pro virtueller Methode für jede Klasse. Und Java ist standardmäßig virtuell ...
Deduplicator
1
@Deduplicator: Das ist richtig, aber Sie können auch argumentieren, dass es einen Overhead gibt, Funktionen / Methoden überhaupt zu haben. Jede Funktion beansprucht Speicher und eine virtuelle Funktion benötigt ein winziges bisschen mehr (ungefähr 4 Bytes).
Bart van Ingen Schenau
@BartvanIngenSchenau Oder 8 Bytes und für jede abgeleitete Klasse erneut. Obwohl beide Schätzungen die Kosten in Java sehr unterschätzen ...
Deduplicator
3
@Deduplicator Diese Kosten gelten jedoch pro Klasse (und pro abgeleiteter Klasse) und nicht pro Instanz.
Craig
7

Unabhängig davon, wie viel Overhead mit dem Aufrufen einer statischen Methode verbunden ist, unabhängig vom schlechten OO-Design und möglichen Kopfschmerzen, verbraucht sie zur Laufzeit nicht weniger Speicher?

Das ist ziemlich kompliziert, aber ich würde "meistens nein" sagen.


quelle
2

Eine Methode sollte "statisch" sein (viele Sprachen verwenden den Begriff "Klassenmethode", was viel besser ist), wenn sie sich nicht auf eine Instanz der Klasse bezieht. Es sollte eine Instanzmethode sein, wenn sie sich auf eine Instanz der Klasse bezieht.

Es ist eine sehr, sehr, sehr schlechte Idee, eine Entwurfsentscheidung zu treffen, die auf der vagen Möglichkeit einer Speichereinsparung anstelle der richtigen Verwendung von Klassenmethoden und Instanzmethoden basiert.

gnasher729
quelle
2
Guter Rat, beantwortet die Frage aber nicht wirklich.
JacquesB
1
Mit der starken Annahme, dass dies eine XY-Frage ist, ist dies der Fall.
Gnasher729
1

Im Prinzip gibt es nur eine Kopie des Codes, unabhängig davon, ob es sich um statische Elementfunktionen handelt oder nicht:

  • In C ++ können Sie dies leicht überprüfen, indem Sie sich die Assembler-Liste des generierten Codes ansehen.
  • Ich bin kein Java-Experte, aber aus der JNI-Spezifikation können Sie ableiten, dass es dieselbe Logik ist: Sie können eine einzelne Referenz auf eine Funktion abrufen und sie für verschiedene Objekte mehrmals aufrufen (indem Sie ihr eine Klassenreferenz als Argument sowie die übergeben Objektreferenz, wenn es sich nicht um eine statische Methode handelt).

Die Verwendung einer statischen oder einer nicht statischen Funktion ändert daher nicht den Speicherbedarf.

In Wirklichkeit gibt es einen sehr sehr kleinen Unterschied, sowohl im generierten Code als auch im Speicher, der während der Ausführungszeit der Funktion auf dem Stapel verbraucht wird:

  • Ein Aufruf der nicht statischen Funktion erfordert die Übergabe einer Referenz / eines Zeigers an das Objekt, auf das die Funktion / Methode angewendet wird. Dies erfordert normalerweise einen zusätzlichen Push und eine Pop-Anweisung in der aufrufenden Sequenz.
  • Ein Aufruf einer statischen Funktion erfordert diesen Overhead nicht.

Dies ist jedoch wirklich vernachlässigbar: Wir sprechen mehr oder weniger von einer oder zwei Maschinenanweisungen im Vergleich zum vollständigen Code. Es ist also definitiv kein Grund zur Sorge.

Die Stapelverbrauchsdifferenz zur Laufzeit ist auf den Zeitpunkt der Ausführung der Funktion und auf die Größe eines Zeigers begrenzt. Dies ist auch vernachlässigbar, es sei denn, Sie denken an eine Funktion, die sehr oft (mehrere Millionen Mal) rekursiv aufgerufen wird.

Abschließend teile ich die Meinung von gnasher729 voll und ganz : Vorzeitige Optimierung ist die Wurzel allen Übels. Wenn die Funktion vom Objekt unabhängig ist, machen Sie sie statisch und das sollte das einzige Kriterium sein.

Christophe
quelle
Ich glaube, der C ++ - Compiler könnte es vermeiden, den Verweis an zu übergeben this, wenn er nicht in der Funktion verwendet wird. In Java könnte JIT dies wahrscheinlich tun.
Oliv
0

Es ist einfach, sich auf Dinge wie die Optimierung der Speichernutzung oder die Reduzierung von Codezeilen zu konzentrieren, aber wenn Sie anfangen, Programme zu warten, die von Benutzern verwendet / unterbrochen werden, und andere Programmierer Funktionen hinzufügen ... und möglicherweise Fehler einführen ... nicht, dass Ihr ursprüngliches Programm Möglicherweise gibt es Fehler, die es nach dem Testen geschafft haben. Sie werden feststellen, dass es möglicherweise wichtiger ist, sich auf die Lesbarkeit / Wartbarkeit des Codes zu konzentrieren. Ich könnte ein Hochgeschwindigkeits-Bit-Array erstellen, um eine Reihe von Datenpunkten zu verfolgen, aber wenn ich dieses Bit-Array nicht in ein Objekt mit verständlichen Zugriffsmethoden einwickle, wird der nächste Programmierer, der diesen Code berührt, ihn wahrscheinlich nicht verwenden, ersetzen oder eine abscheuliche Hexerei betreiben .

Jamy Spencer
quelle