
Heute endete der zwölfte JVM LS Summit. Wie üblich war es ein Hardcore-Event mit technischen Präsentationen zu virtuellen Maschinen und den darauf ausgeführten Sprachen. Wie üblich fand der Gipfel in Santa Clara auf dem Oracle-Campus statt. Wie üblich gibt es viel mehr Leute, die hierher kommen möchten als Orte: Die Teilnehmerzahl überschreitet 120 nicht. Wie üblich gab es kein Marketing, nur Innereien.
Dieser Gipfel ist bereits der dritte für mich und jedes Mal, wenn ich ihn besuche, trotz des schrecklichen Jetlag. Hier können Sie nicht nur Berichte anhören, sondern auch bessere Menschen aus der Welt von JVM kennenlernen, an informellen Gesprächen teilnehmen, Fragen in Workshops stellen und sich im Allgemeinen an großartigen Leistungen beteiligt fühlen.
Wenn Sie nicht am Gipfel teilgenommen haben, spielt es keine Rolle. Die meisten Berichte werden fast unmittelbar nach dem Gipfel auf YouTube veröffentlicht. Eigentlich sind sie schon verfügbar . Um die Navigation zu vereinfachen, werde ich hier kurz alle Berichte und Workshops beschreiben, an denen ich teilgenommen habe.
29. Juli
Hier geht es nicht um die Merkmale der zukünftigen Kompilierung in der Clojure- Sprache, wie viele dachten, sondern lediglich um die Entwicklung der Sprache, die Feinheiten der Codegenerierung und die Probleme, auf die sie stoßen. Beispielsweise stellte sich heraus, dass es in Clojure wichtig ist, lokale Variablen nach der letzten Verwendung zu annullieren, denn wenn der Kopf einer Liste, die träge in einer lokalen Variablen generiert wird, diese umgeht, werden bereits umgangene Knoten möglicherweise nicht vom Garbage Collector erfasst, und das Programm kann mit OutOfMemory abstürzen . Im Allgemeinen gibt der C2-JIT-Compiler die Variablen selbst nach der letzten Verwendung frei, aber der Standard garantiert dies nicht und beispielsweise der HotSpot-Interpreter nicht.
Interessant war auch die Implementierung des dynamischen Dispatchings von Funktionsaufrufen. Ich habe auch erfahren, dass Clojure bis vor kurzem auf JVM 6 abzielte und erst kürzlich auf JVM 8 umgestellt hat. Jetzt betrachten die Compilerautoren invokedynamic.
Das Loom-Projekt ist eine leichte Faser für Java. Vor einem Jahr sprachen Alan und Ron bereits über dieses Projekt, und dann schien es, dass alles sehr gut lief und bald fertig sein würde. Dieses Projekt ist jedoch noch nicht offiziell in Java eingetragen und wird noch in einem separaten Zweig des Repositorys entwickelt. Natürlich stellte sich heraus, dass es notwendig war, viele Details zu regeln.
Viele Standard-APIs von ReentrantLock.lock bis Socket.accept sind bereits für Glasfasern angepasst: Wenn ein solcher Aufruf innerhalb einer Glasfaser erfolgt, wird der Ausführungsstatus gespeichert, der Stapel wird abgewickelt und der Betriebssystem-Thread wird für andere Aufgaben freigegeben, bis ein Ereignis die Glasfaser weckt (z. B. ReentrantLock.unlock). Zum Beispiel funktioniert der gute alte synchronisierte Block jedoch immer noch nicht und es scheint, dass auf eine ernsthafte Umgestaltung der gesamten Synchronisationsunterstützung in der JVM nicht verzichtet werden kann. Ein weiteres Abwickeln des Stapels funktioniert nicht, wenn sich zwischen dem Start der Faser und dem Haltepunkt native Frames im Stapel befinden. In beiden Fällen explodiert nichts, aber die Faser gibt den Strom nicht frei.
Es gibt viele Fragen zum Vergleich von Fibre mit der alten Klasse java.lang.Thread. Vor einem Jahr gab es die Idee, Fibre zu einer Unterklasse von Thread zu machen. Jetzt haben sie es abgelehnt und es zu einer unabhängigen Einheit gemacht, weil das Emulieren des gesamten Verhaltens eines regulären Streams in jeder Faser ziemlich teuer ist. In diesem Fall gibt Thread.currentThread () in der Glasfaser die generierte Mischung zurück und nicht den realen Thread, in dem alles ausgeführt wird. Aber der Haken wird sich ziemlich gut verhalten (obwohl er den Job verlangsamen kann). Die wichtige Idee ist, unter keinen Umständen den tatsächlichen Medienstrom auszugeben, auf dem die Faser innerhalb der Faser läuft. Dies kann gefährlich sein, da sich eine Faser leicht zu einem anderen Faden bewegen kann. Die Täuschung wird fortgesetzt.
Es ist merkwürdig, dass die Projektteilnehmer bereits einige vorbereitende Änderungen in das Haupt-JDK-Repository verschoben haben, um ihnen das Leben zu erleichtern. In Java 13 wurde beispielsweise die Methode doPrivileged vollständig aus nativem Code in Java neu geschrieben, wodurch die Leistung um das 50-fache gesteigert wurde. Warum ist das ein Loom-Projekt? Tatsache ist, dass diese spezielle Methode sehr oft in der Mitte des Stapels auftritt, und obwohl sie ursprünglich war, hörten die Fasern mit diesem Stapel nicht auf. Auf die eine oder andere Weise profitiert das Projekt bereits.
Auf der Projektseite können Sie die Dokumentation lesen und den Quellbaum herunterladen. Außerdem gibt es Binärassemblys , die Sie heute spielen können. Wir hoffen, dass in den kommenden Jahren alles integriert wird.
Brian Goetz - Workshop "Projekt Amber"
Parallel dazu fand ein Workshop über das Loom-Projekt statt, aber ich ging zu Amber. Hier haben wir kurz die Ziele des Projekts und die wichtigsten JEPs besprochen, in denen gearbeitet wird - Pattern Matching , Records und Sealed Types . Dann fiel die ganze Diskussion in die private Frage des Scoping. Ich habe letztes Jahr auf der Joker-Konferenz darüber gesprochen, im Prinzip wurde nichts sehr Neues gesagt. Ich habe versucht, eine Idee mit impliziten Vereinigungstypen wie if(obj instanceof Integer x || obj instanceof Long x) use(x.longValue())
, aber ich habe keine Begeisterung gesehen.
In jeder Hinsicht ein wunderbares Projekt von Google zur Suche nach Rennen, bei dem Daten in Form des Lesens und Schreibens desselben nichtflüchtigen Felds oder Array-Elements aus verschiedenen Streams verwendet werden, ohne dass eine Beziehung zustande kommt, bevor dies geschieht. Das Projekt wurde ursprünglich als LLVM-Modul für nativen Code geschrieben und jetzt für HotSpot angepasst. Dies ist ein offizielles OpenJDK- Projekt mit seiner Mailingliste und seinem Repository.
Laut den Autoren funktioniert das Ding jetzt ganz gut, man kann zusammenbauen und spielen. Darüber hinaus findet sie Rennen nicht nur im Java-Code, sondern auch im Code nativer Bibliotheken. Rennen im Code der virtuellen Maschine selbst werden nicht durchsucht, da dort alle Synchronisationsprimitive auf ihre eigene Weise geschrieben sind und TSan sie nicht erkennen kann. Laut den Autoren gibt TSan keine falsch positiven Ergebnisse.
Das Hauptproblem ist die Leistung. Jetzt ist nur der Interpreter für Java-Code instrumentiert, die JIT-Kompilierung ist vollständig deaktiviert und der bereits langsame Interpreter wird mehrmals langsamer. Wenn Sie jedoch über genügend Ressourcen verfügen (Google verfügt natürlich über genügend Ressourcen), können Sie Ihre Testsuiten gelegentlich mit TSan fahren. Es ist auch geplant, die JIT um Instrumente zu erweitern, dies ist jedoch eine viel ernstere Intervention in der JVM.
Jemand fragte, ob das Deaktivieren der JIT-Kompilierung das Ergebnis nicht beeinflusst, da einige Rennen möglicherweise nicht auf dem Interpreter angezeigt werden. Der Redner schloss diese Möglichkeit nicht aus, sagte aber, dass sie bereits eine große Anzahl von Rennen gefunden hätten, deren Harken sehr lange dauern würde. Seien Sie also vorsichtig, wenn Sie Ihr Projekt unter TSan ausführen: Möglicherweise finden Sie die unangenehme Wahrheit heraus.
Jeder wartet auf Werttypen in Java, aber niemand weiß, wann sie erscheinen werden. Die Bewegungen werden jedoch immer ernster. Es gibt bereits Test- Binär-Assemblys mit dem aktuellen L2-Meilenstein. In den aktuellen Plänen wird das gesamte Walhalla den Meilenstein L100 erreichen, aber die Autoren sind immer noch optimistisch und glauben, dass mehr als zwei Prozent erreicht wurden.
Aus sprachlicher Sicht haben wir also Klassen mit dem Inline-Modifikator, die von der virtuellen Maschine auf besondere Weise verarbeitet werden. Instanzen solcher Klassen können in andere Objekte eingebettet werden, und flache Arrays, die Instanzen von Inline-Klassen enthalten, sind ebenfalls möglich. Die Instanz hat keinen Header, was bedeutet, dass es keine Identität gibt. Der Hash-Code wird durch Felder berechnet, ==
auch durch Felder. Ein Synchronisierungsversuch oder Object.wait()
für eine solche Klasse löst eine IllegalMonitorStateException aus. Das Schreiben von null
in eine Variable dieses Typs funktioniert natürlich nicht. Die Autoren bieten jedoch eine Alternative: Wenn Sie einen Inline-Klassenpunkt deklariert haben, können Sie ein Feld oder eine Variable vom Typ (Überraschung-Überraschung!) Point?
deklarieren Point?
und dann wird es ein vollwertiges Objekt auf dem Heap geben (wie Boxen) mit einem Header, einer Identität und einer null
hineinpassen.
Ernsthafte offene Fragen bleiben die Spezialisierung von Generika und die Migration vorhandener Klassen (z. B. Optional
) zu einer Inline-Klasse, um den vorhandenen Code nicht zu beschädigen (ja, in Variablen vom Typ Optional
null
). Trotzdem taucht das Bild auf und die Lücke ist sichtbar.
Es war eine Überraschung für mich, dass derselbe Neil Gufter, Co-Autor der ursprünglichen Java-Puzzler, jetzt bei Microsoft zur .Net-Laufzeit arbeitet. Es war auch eine Überraschung, einen Bericht über die CLR (die sogenannte .Net-Laufzeit) auf der JVM-LS zu sehen. Es ist jedoch immer nützlich, die Erfahrungen von Kollegen aus anderen Welten kennenzulernen. Der Bericht spricht über die verschiedenen Referenzen und Zeiger in der CLR, über die Bytecode-Anweisungen, die für Werttypen verwendet werden, und darüber, wie schön spezialisierte verallgemeinerte Funktionen wie Reduzieren sind. Es war interessant zu erfahren, dass eines der Ziele von Werttypen in .Net eine Interaktion mit nativem Code ist. Aus diesem Grund ist die Position von Feldern in Werttypen streng festgelegt und kann ohne Transformationen auf eine Struktur projiziert werden. Die JVM hatte noch nie eine solche Aufgabe und was mit dem nativen Interop zu tun ist - siehe unten.
Aktualisieren Sie den Bericht des letzten Jahres erneut . Wieder ist die Frage, warum sie noch nichts veröffentlicht haben, wenn vor einem Jahr alles ziemlich gut aussah.
Ein Vektor ist eine Sammlung mehrerer Zahlen, die in der Hardware durch ein einzelnes Vektorregister wie zmm0 für AVX512 dargestellt werden können. In Vektoren können Sie Daten aus Arrays laden, Operationen wie elementweise Multiplikation ausführen und sie zurückwerfen. Alle Operationen, für die Prozessoranweisungen vorhanden sind, werden vom JIT-Compiler in diese Anweisungen integriert. Die Anzahl der Operationen ist einfach riesig. Wenn etwas fehlt, wird eine alternative langsame Implementierung verwendet. Vektorzwischenobjekte werden idealerweise nicht erstellt, die Escape-Analyse funktioniert. Alle Standard-Rechenalgorithmen werden mit einem Knall vektorisiert, wobei die gesamte Leistung Ihres Prozessors genutzt wird.
Leider fällt es den Autoren schwer, keine Valgalla zu haben: Die Fluchtanalyse ist fragil und funktioniert möglicherweise nicht einfach. Diese Vektoren müssen einfach Inline-Klassen sein, dann verschwinden alle Probleme. Es ist unklar, ob diese API überhaupt vor der ersten Version von Valgalla veröffentlicht werden kann. Es scheint viel mehr bereit. Zu den Problemen zählen Schwierigkeiten bei der Unterstützung des Codes. Es gibt viele sich wiederholende Teile für unterschiedliche Registergrößen und unterschiedliche Datentypen, sodass der größte Teil des Codes aus Vorlagen generiert wird und es weh tut, ihn zu verwalten.
Die Verwendung ist auch unvollkommen. In Java gibt es keine Operatorüberladung, daher sieht die Mathematik hässlich aus: Anstelle von max(va-vb*42, 0)
Sie va.lanewise(SUB, vb.lanewise(MUL, 42)).lanewise(MAX, 0)
. Es wäre schön, Zugang zu AST-Lambdas wie in C # zu haben. Dann wäre es möglich, eine benutzerdefinierte Lambda-Operation wie MYOP = binOp((va, vb) -> max(va-vb*42, 0))
zu generieren und zu verwenden.
30. Juli
Der zweite Tag verging unter der Flagge der Zusammenstellung.
Ein IBM-Mitarbeiter, Mitglied des JVM OpenJ9-Projekts, berichtet über seine Erfahrungen mit der JIT- und AOT-Kompilierung. Es gibt immer Probleme: JIT ist ein langsamer Start, weil es sich erwärmt. CPU-Kosten für die Kompilierung. AOT - suboptimale Leistung aufgrund des Fehlens eines Profils (es ist möglich, ein Profil zu erstellen, aber nicht trivial und nicht immer stimmt das Profil während der Kompilierung mit dem Profil bei der Ausführung überein), es ist schwieriger zu verwenden, an die Zielplattform, das Betriebssystem und den Garbage Collector zu binden. Einige der Probleme können durch Kombinieren von Ansätzen gelöst werden: Beginnen Sie mit AOT-kompiliertem Code und beenden Sie dann mit JIT. Eine gute Alternative zu all dem ist das Zwischenspeichern von JIT. Wenn Sie viele virtuelle Maschinen haben (Hallo, Microservices), wenden sich alle an einen separaten Dienst - den JIT-Compiler (ja, JITaaS), bei dem alles wie ein Erwachsener ist, Orchestrierung, Lastausgleich. Dieser Service wird kompiliert. Sehr oft kann er einer bestimmten Methode vorgefertigten Code geben, da diese Methode bereits auf einer anderen JVM kompiliert wurde. Dies verbessert das Aufwärmen erheblich, entfernt den Ressourcenverbrauch aus Ihrem JVM-Dienst und reduziert im Allgemeinen den gesamten Ressourcenverbrauch.
Im Allgemeinen könnte JITaaS das nächste Schlagwort in der JVM-Welt sein. Leider habe ich nicht verstanden, ob dies gerade gespielt werden kann oder ob es sich noch um eine geschlossene Entwicklung handelt.
GraalVM Native Image ist eine Java-Anwendung, die in nativen Code kompiliert wurde und ohne JVM ausgeführt wird (im Gegensatz zu Modulen, die mit einem AOT-Compiler wie jaotc kompiliert wurden). Genauer gesagt ist dies keine Java-Anwendung. Um richtig zu arbeiten, benötigt er eine geschlossene Welt, dh der gesamte Code sollte in der Kompilierungsphase sichtbar sein, keine Class.forName. Sie können Reflektions- und Methodenhandles verwenden, aber beim Kompilieren müssen Sie genau angeben, welche Klassen und Methoden durch Reflektion verwendet werden.
Eine weitere lustige Sache ist die Klasseninitialisierung. Viele Klassen werden während der Kompilierung initialisiert. Das heißt, Ihre statischen Felder werden vom Compiler standardmäßig berechnet und das Ergebnis wird in das zusammengestellte Image geschrieben. Wenn Sie die Anwendung starten, wird es einfach gelesen. Dies ist erforderlich, um eine bessere Kompilierungsqualität zu erzielen: Eine konstante Faltung kann durchgeführt werden, wenn die Werte der statischen Felder dem Compiler bekannt sind. Mit JIT ist alles in Ordnung, der Interpreter führt eine statische Initialisierung durch und dann können Sie die Konstanten kennen, die Sie kompilieren können. Und wenn Sie eine native Anwendung erstellen, müssen Sie einen Trick machen. Dies führt natürlich zu lustigen psychedelischen Effekten. Daher werden Klassen normalerweise in der Reihenfolge initialisiert, in der auf sie zugegriffen wird. Während der Kompilierung ist diese Reihenfolge unbekannt und eine Initialisierung in einer anderen ist möglich. Wenn zwischen Klasseninitialisierern Zirkelverweise vorhanden sind, können Sie den Unterschied im Verhalten des JVM-Codes und im nativen Image erkennen.
Workshop Schatzl - Hotspot GC.
Sortierte alle Schmerzen aus, die mit den Müllsammlern verbunden sind. Leider habe ich am meisten zugehört. Ich erinnere mich, dass der Speicherabruf des Betriebssystems diskutiert wurde, einschließlich des ekelhaften Xmx für alle. Es gibt gute Nachrichten: In Java 13 wurde eine neue Option -XX hinzugefügt: SoftMaxHeapSize. Bisher wird es nur vom ZGC-Kollektor unterstützt, aber G1 kann auch aufholen. Es legt eine Grenze für die Größe des Heaps fest, die nur in Notfallsituationen überschritten werden sollte, wenn dies nicht anders funktioniert. Auf diese Weise können Sie ein großes Xmx (z. B. gleich der Größe des gesamten RAM) und eine angemessene SoftMaxHeapSize festlegen. Dann behält sich die JVM die meiste Zeit selbst bei, aber bei Spitzenlast wird OutOfMemoryError immer noch nicht ausgelöst, sondern es wird mehr Speicher vom Betriebssystem benötigt. Wenn die Last abfällt, kehrt der Speicher zurück.
Microsoft Mei-Chin Tsai sprach über die Funktionen der JIT- und AOT-Kompilierung in der CLR. Die AOT-Kompilierung wurde für sie schon lange entwickelt, aber ursprünglich (ngen.exe) wurde sie auf der Zielplattform ausgeführt, ähnlich wie beim ersten Start (wenn Sie Windows haben, suchen Sie im Windows-Ordner nach den Dateien * .ni.dll). Dateien werden abhängig von der Version des lokalen Windows und sogar von anderen DLL-ek abgerufen. Wenn die Abhängigkeit aktualisiert wird, müssen dementsprechend alle nativen Module neu kompiliert werden. In der zweiten Generation (crossgen) haben Autoren Anwendungen und Module relativ unabhängig von Hardware- und Betriebssystemversionen und -abhängigkeiten vorkompiliert. Dies verlangsamte den Code, da Abhängigkeitsaufrufe nun ehrlich virtuell erfolgen mussten. Dieses Problem wurde gelöst, indem JIT angeschlossen und der Hotcode während der Anwendung neu kompiliert wurde. Dann sprachen wir über mehrstufige (gestufte) Kompilierung (es scheint, dass diese in der CLR noch in den Kinderschuhen steckt, während sie sich seit mindestens zehn Jahren in Java entwickelt) und über zukünftige Pläne, AOT wirklich plattformübergreifend zu machen.
Alibaba-Kollegen stellten ihre Herangehensweise an das JVM-Aufwärmproblem vor. Sie verwenden die JVM für viele Webdienste. Im Prinzip ist ein sehr schneller Start nicht so wichtig, da der Balancer immer warten kann, bis der Computer hochfährt, und erst dann Anforderungen an ihn sendet. Das Problem ist jedoch, dass sich der Computer nicht ohne Anforderungen aufwärmt: Der Code, der die Logik zum Verarbeiten von Anforderungen beschreibt, wird nicht aufgerufen, was bedeutet, dass er nicht kompiliert wird. Es wird kompiliert, wenn die ersten Anforderungen eintreffen, dh unabhängig davon, wie lange der Balancer wartet, tritt bei den ersten Anforderungen ein Leistungsfehler auf. Zuvor haben sie versucht, dieses Problem zu lösen, indem sie gefälschte Anfragen an den bevorstehenden Dienst gesendet haben, bevor sie echte Anfragen an diesen gesendet haben. Der Ansatz ist interessant, aber es ist ziemlich schwierig, einen solchen gefälschten Stream zu generieren, der die Kompilierung des gesamten erforderlichen Codes bewirken würde.
Ein separates Problem ist die Deoptimierung. In den ersten tausend Abfragen, von denen eine immer entlang des ersten Zweigs ging, warf der JIT-Compiler im Allgemeinen den zweiten und fügte dort eine Deoptimierungsfalle ein, um die Codegröße zu verringern. Die 1001. Anfrage ging jedoch an den zweiten Zweig, die Deoptimierung funktionierte und die gesamte Methode ging an den Dolmetscher. Während die Statistiken erneut kompiliert werden, während die Methode vom C1-Compiler und dann vom C2-Compiler über das vollständige Profil kompiliert wird, tritt eine Verlangsamung auf. Und dann in der gleichen Methode eine andere, if
deoptimiert werden kann, und alles wird auf eine neue gehen.
JWarmUp löst das Problem wie folgt. Während der ersten Ausführung des Dienstes wird einige Minuten lang ein Kompilierungsprotokoll geschrieben: Es zeichnet auf, welche Methoden kompiliert wurden, und die erforderlichen Profilinformationen nach Zweigen, Typen usw. Wenn dieser Dienst sofort nach dem Start neu gestartet wird, werden alle Klassen aus dem Protokoll initialisiert und die protokollierten Methoden kompiliert unter Berücksichtigung des vorherigen Profils. Infolgedessen funktioniert der Compiler beim Start gut. Danach beginnt der Balancer, Anforderungen an diese JVM zu senden. Zu diesem Zeitpunkt ist der gesamte heiße Code, den sie bereits kompiliert hat.
Es ist erwähnenswert, dass das Schnellstartproblem hier nicht gelöst ist. Ein Start kann sogar noch langsamer sein, da viele Methoden kompiliert werden, von denen einige möglicherweise nur wenige Minuten nach dem Start erforderlich sind. Das Protokoll stellt sich jedoch als wiederverwendbar heraus: Im Gegensatz zu AOT können Sie den Dienst auf einer anderen Architektur oder mit einem anderen Garbage Collector erhöhen und das vorherige Protokoll wiederverwenden.
Autoren haben lange versucht , JWarmUp in OpenJDK zu pushen. Bisher erfolglos, aber die Arbeit bewegt sich. Die Hauptsache ist, dass Sie auf dem Code Review-Server auf einen vollwertigen Patch zugreifen können, sodass Sie ihn problemlos auf die HotSpot-Quellen anwenden und die JVM selbst mit JWarmUp erstellen können.
Dies ist ein Forschungsbericht aus Manchester, aber die Autoren behaupten, dass das Projekt an einigen Stellen bereits umgesetzt wurde. Es ist auch ein Add-On für OpenJDK, mit dem sich bestimmte Java-Codes ganz einfach auf GPU, iGPU, FPGA übertragen oder einfach auf die Kerne des Prozessors parallelisieren lassen. Zum Kompilieren auf der GPU verwenden sie GraalVM, in dem sie ihr Backend - TornadoJIT - erstellt haben. Eine korrekt geschriebene Java-Methode wird transparent an das entsprechende Gerät gesendet. Die Kompilierung auf FPGA kann zwar mehrere Stunden dauern, aber wenn Ihre Aufgabe als Monat betrachtet wird, warum dann nicht? Einige Benchmarks (zum Beispiel die diskrete Fourier-Transformation) sind mehr als hundertmal schneller als Java, was im Prinzip erwartet wird. Das Projekt wird vollständig auf GitHub hochgeladen, wo Sie auch wissenschaftliche Veröffentlichungen zu diesem Thema finden.
Alles das gleiche Lied - ein langjähriges Projekt, jede Gipfelpräsentation, vor einem Jahr sah alles ziemlich fertig aus, aber es gab noch keine Veröffentlichung. Es stellte sich heraus, dass sich der Fokus seitdem verschoben hat.
Die Idee des Projekts ist eine verbesserte Interaktion mit nativem Code. Jeder weiß, wie schmerzhaft es ist, JNI zu verwenden. Es tut sehr weh. Das Panama-Projekt hebt diesen Schmerz auf: Mit Jextract werden Java-Klassen aus den * .h-Dateien der nativen Bibliothek generiert, die durch Aufrufen nativer Methoden sehr praktisch sind. Auf der C / C ++ - Seite müssen Sie überhaupt keine einzige Zeile schreiben. Außerdem wurde alles viel schneller: Der Overhead bei Aufrufen von Java-> native und native-> Java ging zeitweise zurück. Was willst du mehr?
Es gibt ein Problem, das schon seit einiger Zeit besteht - das Übertragen von Datenarrays auf nativen Code. Bisher ist die empfohlene Methode DirectByteBuffer, bei der viele Probleme auftreten. Eine der schwerwiegendsten ist die nicht verwaltete Lebensdauer (der Puffer verschwindet, wenn der Garbage Collector das entsprechende Java-Objekt aufnimmt). Aufgrund dieses und anderer Probleme verwenden Benutzer Unsafe, die mit der gebotenen Sorgfalt problemlos die gesamte virtuelle Maschine ablegen können.
Dies bedeutet, dass Sie einen neuen normalen Speicherzugriff außerhalb des Java-Heaps benötigen. Zuordnung, strukturierte Accessoren, explizite Entfernung. Strukturierte Accessoren - damit Sie die Offsets nicht selbst berechnen müssen, wenn Sie beispielsweise struct { byte x; int y; }[5]
struct { byte x; int y; }[5]
struct { byte x; int y; }[5]
. Stattdessen beschreiben Sie einmal das Layout dieser Struktur und führen dann beispielsweise VarHandle
, das alle x
lesen kann, indem Sie über y
springen. In diesem Fall sollte es natürlich immer eine Grenzprüfung geben, wie bei normalen Java-Arrays. Darüber hinaus sollte der Zugang zu einem bereits geschlossenen Bereich verboten werden. Und dies stellt sich als nicht triviale Aufgabe heraus, wenn wir die Leistung auf der unsicheren Ebene halten und den Zugriff von mehreren Threads aus ermöglichen möchten. Kurz gesagt, schauen Sie sich das Video an, sehr interessant.
Workshop: Vladimir Kozlov - Metropolis-Projekt
Das Metropolis-Projekt kombiniert alle Versuche, Teile der JVM in Java neu zu schreiben. Sein Hauptteil ist heute der Graal-Compiler. In den letzten Jahren hat es sich sehr gut entwickelt und es ist bereits die Rede von einem vollständigen Ersatz für das alternde C2. Früher gab es ein Bootstrap-Problem: Der Gral begann langsam, weil er selbst JIT-kompiliert oder interpretiert werden musste. Dann erschien die AOT-Kompilierung (ja, das Hauptziel des AOT-Kompilierungsprojekts ist der Bootstrap des Grals selbst). Aber mit AOT frisst der Gral einen anständigen Teil des Heaps einer Java-Anwendung auf, die ihren Heap möglicherweise nicht wirklich teilen möchte. Jetzt haben wir gelernt, den Gral mithilfe von Graal Native Image in eine native Bibliothek umzuwandeln, wodurch wir den Compiler letztendlich vom allgemeinen Heap isolieren konnten. Bei der Spitzenleistung des vom Grail kompilierten Codes treten bei einigen Benchmarks immer noch Probleme auf. Zum Beispiel bleibt der Gral in Bezug auf Intrinsik und Vektorisierung hinter C2 zurück. Dank der sehr leistungsfähigen Inlining- und Escape-Analyse wird C2 im Funktionscode, in dem viele unveränderliche Objekte und viele kleine Funktionen erstellt werden, einfach unterbrochen. Wenn Sie auf den Felsen schreiben und den Gral immer noch nicht verwenden, führen Sie ihn aus. Darüber hinaus ist es in den neuesten Versionen von JDK ziemlich trivial, ein paar Schlüssel zu machen, alles ist bereits im Kit enthalten.
31. Juli
Kevin Bourrillion - Nullness Annotations für Java
Kevin kündigte ein neues Projekt an, bat jedoch darum, nicht öffentlich zu sprechen und keine Aufzeichnung seiner Rede auf YouTube zu veröffentlichen. Tut mir leid. , .
Sorbet (!) Ruby, Ruby . , Stripe Ruby , , . , .
Lightning Talks
- . Remi Forax , , . , :
, - , .
ML AI , . , Facebook — getafix , --, , . . , , . , , .
. . OpenJDK Committer Workshop.