Was ist die maximale Anzahl von Lambdas, die in einer Java-Klasse verwendet werden?


Grzegorz Piwowarek:

Dies ist eine rein konzeptionelle Frage.

Lambdas in Java 8 werden in Methoden konvertiert, die mit dem aufgerufen werden invokedynamic.

Wenn es eine JVM-Beschränkung für eine maximale Anzahl von Methoden gibt, die eine Klasse haben kann, bedeutet dies, dass die maximale Anzahl von Lambdas, die in einer Klasse verwendet werden, ebenfalls streng durch JVM begrenzt ist?

Ist diese Frage dieser ziemlich ähnlich? Wie viele Methoden kann eine Java-Klasse maximal haben?

Holger:

Die Java-Sprachspezifikation schreibt keine Begrenzung vor, daher gibt es nur technische Einschränkungen. Die Spezifikation schreibt auch keine bestimmte kompilierte Form vor, sodass selbst die technischen Einschränkungen verschwommen sind.

Lambda-Ausdrücke werden in Methoden der Klassendatei kompiliert, die den Hauptteil des Lambda-Ausdrucks enthalten. Dies ist jedoch nicht unbedingt erforderlich. Insbesondere könnte ein einfacher Ausdruck des Formulars foo -> bar(foo)wie Methodenreferenzen kompiliert werden. Ferner könnten identische Lambda-Ausdrücke mit derselben Methode kompiliert werden. Dies ist eine Optimierung, die derzeit nicht stattfindet und das Debuggen erschwert, aber im Prinzip zulässig ist.

Ein intelligenter Compiler könnte auch damit beginnen, Hilfsklassen zu generieren, die Lambda-Körper hosten, wenn er den unwahrscheinlichen Fall erkennt, dass das Limit bald erreicht wird.

Bei der aktuellen einfachen Implementierung wirkt sich die maximale Anzahl von Methoden, dh 65535, auf die maximale Anzahl möglicher Lambda-Ausdrücke aus. Dies bedeutet jedoch nicht, dass wir 65535-Lambda-Ausdrücke erstellen können.

Beispielsweise muss mindestens eine (Quellcode-) Methode vorhanden sein, die den Lambda-Ausdruck enthält, wodurch die Instanz der Funktionsschnittstelle erstellt wird. Die minimale Anweisungsgröße einer Erstellungssite ist eine alleinige invokedynamicAnweisung mit fünf Bytes¹. Da die maximale Codegröße einer Methode 65535 beträgt und wir mindestens einen By für die returnAnweisung benötigen , kann 65534/5 == 13106eine Methode höchstens Lambda-Ausdrücke enthalten. Um mehr zu erstellen, müssen sie in verschiedenen Methoden platziert werden, wodurch die Anzahl der Methoden verringert wird verfügbar für Lambda-Ausdrücke. Sie können arbeiten-um diese durch verschachtelte Lambda - Ausdrücke verwenden, das heißt x -> y -> z, aber auch Verschachtelung hat praktische Grenzen .

Die aktuellen Compiler verwenden Namensschemata, die für jede Synthesemethode eindeutige Namen erzeugen. Daher benötigen sie einzelne konstante Pooleinträge. Mit eindeutigen Implementierungsmethoden benötigt jede Lambda-Erstellungssite einen Eintrag für den Namen, den Namen und den Typ, der sich auf den Namen bezieht, ein „MethodRef“, das sich auf den Namen und den Typ bezieht, und die (immer dieselbe) Deklarationsklasse, ein Methodenhandle, das sich auf die „ MethodRef ”und ein aufgerufener dynamischer Eintrag, der sich auf das Methodenhandle bezieht. Dies ergibt insgesamt fünf konstante Pooleinträge pro Lambda-Ausdruck. Da der konstante Pool auf 65534 Einträge beschränkt ist und wir einige Einträge für andere Zwecke benötigen, beträgt die Berechnung 65500/5, sodass bei den aktuellen Compiler-Implementierungen die maximale Anzahl von Lambda vorliegt Ausdrücke sind 13.100 . Vorausgesetzt, sie haben alle die gleiche Signatur…

In einem Praxistest mit javac(1.8u111) konnte ich eine Klassendatei mit 13.098 Lambda-Ausdrücken derselben Signatur und sogar genau 13.100 mit deaktivierter Generierung von Debugging-Symbolen kompilieren, bevor der Fehler „zu viele Konstanten“ auftrat. In dieser Testklasse habe ich die Lambda-Ausdrücke in zwei Konstruktoren eingefügt, da mindestens ein Konstruktor ohnehin vorhanden sein muss und beide den Namenseintrag gemeinsam nutzen können. Ich denke, mit einem Standard-Compiler kann man nicht mehr erreichen.


Wenn Sie die durch das Namensschema auferlegte Einschränkung aufheben möchten, müssen Sie dennoch die Regel einhalten, dass jede Methode unterscheidbar sein muss, damit sie sich zumindest durch Name oder Signatur von anderen Methoden unterscheidet. Wenn Sie versuchen, das theoretische Maximum zu erreichen, müssen Sie n verschiedene Methodennamen mit m verschiedenen Signaturen kombinieren , um dies zu ermöglichenn×mUnterschiedliche Methoden, daher erfordern 65535-Methoden mindestens 256 Namenseinträge und 256 Signatureinträge. Sie haben immer noch eindeutige Namens- und Typkombinationen, sodass Sie die anderen vier Einträge pro Lambda-Ausdruck benötigen, was zu 16.247 möglichen Lambda-Ausdrücken führt. Da dies weit weniger als 65535 sind, können Sie mit geringeren Namens- und Typkombinationen umgehen, dh 128 Namen mit 128 Signaturen kombinieren, mit mehr Einträgen für die Erstellungsseiten, dh mit 16311 möglichen Lambda-Ausdrücken. Noch mehr, wenn Sie die Signaturzeichenfolgen als Methodennamen missbrauchen (was auf Bytecode-Ebene funktioniert, solange die Signaturen keine Referenztypen enthalten).

Für (signifikant) mehr müssen Sie aufhören, unterschiedliche Methoden für jeden Lambda-Ausdruck zu generieren.


¹ das würde einen gültigen Bytecode ergeben. Auf Quellcodeebene sind Lambda-Ausdrücke keine Anweisungen, daher ist mehr Code erforderlich, z. B. eine Zuweisung zu einer Variablen.

Verwandte Artikel


Was ist die maximale Anzahl von Verbindungen?

Amberlamps Da die nativen RethinkDB-Treiber das Verbindungspooling noch nicht unterstützen, habe ich mich gefragt, wie viele Verbindungen zum RethinkDB-Server maximal vorhanden sind. Jorge Silva Auf diese Frage gibt es einige Antworten: Verbindungspooling Es g