Wie hoch ist die maximale Anzahl von Lambdas in einer Java-Klasse?

8

Dies ist eine rein konzeptionelle Frage.

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

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 Frage ziemlich ähnlich? Wie viele Methoden können maximal verwendet werden? Java-Klasse kann?

haben     
Grzegorz Piwowarek 29.11.2016, 08:18
quelle

1 Antwort

7

Die Java Language Specification schreibt kein Limit vor, daher haben wir nur technische Einschränkungen. Die Spezifikation schreibt auch keine bestimmte kompilierte Form vor, so dass selbst die technischen Beschränkungen unscharf sind.

Lambda-Ausdrücke werden in Methoden der Klassendatei kompiliert, die den Rumpf des Lambda-Ausdrucks enthalten, aber dies ist nicht unbedingt erforderlich. Vor allem kann der einfache Ausdruck der Form foo -> bar(foo) wie Methodenreferenzen kompiliert werden. Außerdem könnten identische Lambda-Ausdrücke mit der gleichen Methode kompiliert werden. Dies ist eine Optimierung, die derzeit nicht stattfindet und das Debugging erschwert, aber im Prinzip erlaubt ist.

Außerdem könnte ein intelligenter Compiler Hilfsklassen erzeugen, die Lambda-Körper hosten, wenn er den unwahrscheinlichen Fall feststellt, dass das Limit erreicht wird.

Bei der aktuellen geradlinigen Implementierung wirkt sich die maximale Anzahl von Methoden, d. h. 65535, auf die maximale Anzahl möglicher Lambda-Ausdrücke aus, dies bedeutet jedoch nicht, dass 65535 Lambda-Ausdrücke erstellt werden können.

Zum Beispiel muss mindestens eine (Quellcode) -Methode vorhanden sein, die den Lambda-Ausdruck enthält, der die Instanz der funktionalen Schnittstelle erzeugt. Die minimale Befehlsgröße einer Erzeugungsstelle ist eine einzige invokedynamic -Anweisung mit fünf Bytes¹. Da die maximale Codegröße einer Methode 65535 ist und wir mindestens eine für die Anweisung return benötigen, können maximal 65534/5 == 13106 Lambda-Ausdrücke in einer Methode vorhanden sein. Wenn Sie also mehr erstellen möchten, müssen Sie sie in verschiedenen Methoden platzieren , die Anzahl der für Lambda-Ausdrücke verfügbaren Methoden zu reduzieren. Sie können dies umgehen, indem Sie verschachtelte Lambda-Ausdrücke verwenden, dh x -> y -> z , , aber selbst die Verschachtelung hat praktische Grenzen .

Die aktuellen Compiler verwenden Benennungsschemata, die eindeutige Namen für jede synthetische Methode erzeugen, so dass sie individuelle Einträge für den konstanten Pool benötigen. Mit eindeutigen Implementierungsmethoden benötigt jeder Lambda-Erstellungsstandort einen Eintrag für den Namen, den Namen und den Typ, der sich auf den Namen bezieht, eine "MethodRef", die sich auf den Name & amp; typ bezieht, und die (immer dieselbe) Deklarationsklasse, ein Methodenhandle Bezug auf den "MethodRef" und einen aufgerufenen dynamischen Eintrag, der sich auf das Methodenhandle bezieht. Dies ergibt insgesamt fünf konstante Pool-Einträge pro Lambda-Ausdruck und da der Konstanten-Pool auf 65534 Einträge beschränkt ist und wir einige Einträge für andere Zwecke benötigen, ist die Berechnung 65500/5, also bei den aktuellen Compilerimplementierungen das Maximum Anzahl der Lambda-Ausdrücke ist 13.100 . Angenommen, sie haben alle die gleiche Signatur ...

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

Wenn Sie die durch das Benennungsschema auferlegte Einschränkung aufheben wollen, müssen Sie sich weiterhin an die Regel halten, dass jede Methode unterscheidbar sein muss, so dass sie sich entweder durch Name oder Signatur von anderen Methoden unterscheiden muss. Wenn Sie versuchen, das theoretische Maximum zu erreichen, müssen Sie n eindeutige Methodennamen mit m eindeutigen Signaturen kombinieren, um n×m distinct-Methoden zuzulassen, also werden 65535-Methoden benötigt mindestens 256 Namenseinträge und 256 Signatureinträge. Sie haben immer noch eindeutige Kombinationen aus Name und Typ. Daher benötigen Sie die anderen vier Einträge pro Lambda-Ausdrücke und enden mit 16.247 möglichen Lambda-Ausdrücken. Da diese weit weniger als 65535 sind, können Sie mit kleineren Kombinationen aus Name und Typ umgehen, d. H. 128 Namen mit 128 Signaturen kombinieren, mehr Einträge für die Erzeugungsstellen haben, d. H. 16311 möglichen Lambda-Ausdruck haben. Etwas mehr, wenn Sie die Signaturzeichenfolgen als Methodennamen missbrauchen (was auf der Bytecodeebene funktioniert, solange die Signaturen keine Referenztypen enthalten).

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

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

    
Holger 29.11.2016, 13:21
quelle

Tags und Links