
Belum lama berselang, Oracle merilis rilis pertama proyek GraalVM (https://www.graalvm.org/). Rilis ini segera diberi nomor 19.0.0, tampaknya untuk meyakinkan bahwa proyek tersebut matang dan siap digunakan dalam aplikasi serius. Salah satu bagian dari proyek ini:
Substrate VM adalah kerangka kerja yang memungkinkan Anda untuk mengubah aplikasi Java menjadi file yang dapat dieksekusi asli (serta pustaka asli yang dapat dihubungkan dalam aplikasi yang ditulis, misalnya, dalam C / C ++). Fitur ini sejauh ini telah dinyatakan eksperimental. Perlu dicatat juga bahwa aplikasi Java asli memiliki beberapa batasan: Anda harus mendaftar semua sumber daya yang digunakan untuk memasukkannya ke dalam program asli; Anda perlu mendaftar semua kelas yang akan digunakan dengan refleksi dan batasan lainnya. Daftar lengkap diberikan di sini oleh
Batasan Java Gambar Asli . Setelah mempelajari daftar ini, pada prinsipnya dapat dipahami bahwa batasannya tidak begitu signifikan sehingga tidak mungkin untuk mengembangkan aplikasi yang lebih kompleks daripada hellwords. Saya menetapkan tujuan ini: mengembangkan program kecil yang memiliki server web built-in, menggunakan database (melalui perpustakaan ORM) dan mengkompilasi menjadi biner asli yang dapat berjalan pada sistem tanpa mesin Java diinstal.
Saya akan bereksperimen di Ubuntu 19.04 (Intel Core i3-6100 CPU @ 3.70GHz × 4).
Instal GraalVM
Instalasi GraalVM mudah dilakukan menggunakan
SDKMAN . Perintah instalasi GraalVM:
sdk install java 19.0.0-grl
OpenJDK GraalVM CE 19.0.0 akan diinstal, CE adalah Edisi Komunitas. Ada juga Enterprise Edition (EE), tetapi edisi ini perlu diunduh dari Oracle Technology Network, tautannya ada di halaman
GraalVM Downloads .
Setelah menginstal GraalVM, sudah menggunakan manajer pembaruan komponen gu dari GraalVM, saya menginstal dukungan kompilasi di binary asli -
gu install native-image
Semuanya, alat kerja sudah siap, sekarang Anda dapat mulai mengembangkan aplikasi.
Aplikasi asli sederhana
Sebagai sistem build, saya menggunakan Maven. Untuk membuat binari asli, ada plugin pakar:
native-image-maven-plugin <build> <plugins> <plugin> <groupId>com.oracle.substratevm</groupId> <artifactId>native-image-maven-plugin</artifactId> <version>${graal.version}</version> <executions> <execution> <goals> <goal>native-image</goal> </goals> <phase>package</phase> </execution> </executions> <configuration> <imageName>nativej</imageName> <buildArgs> --no-server </buildArgs> </configuration> </plugin> </plugins> </build>
Masih diperlukan untuk mengatur kelas utama aplikasi. Ini dapat dilakukan baik dalam native-image-maven-plugin, dan dengan cara tradisional, melalui:
maven-jar-plugin <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-jar-plugin</artifactId> <version>2.4</version> <configuration> <archive> <manifest> <mainClass>nativej.Startup</mainClass> </manifest> </archive> </configuration> </plugin>
Buat kelas utama:
Startup.java public class Startup { public static void main(String[] args) { System.out.println("Hello world!"); } }
Sekarang Anda dapat menjalankan perintah pakar untuk membangun aplikasi:
mvn clean package
Membangun biner asli di komputer saya membutuhkan waktu 35 detik. Akibatnya, file biner berukuran 2,5 MB diperoleh di direktori target. Program tidak memerlukan mesin Java yang terinstal dan berjalan pada mesin-mesin di mana Java tidak ada.
Tautan repositori:
Github: native-java-helloworld-demo .
JDBC Postgres Driver
Jadi, aplikasi sederhana berfungsi, menampilkan "Hello world". Tidak ada solusi yang dibutuhkan. Saya akan mencoba untuk naik satu tingkat lebih tinggi: Saya akan menghubungkan driver JDBC Postgres untuk meminta data dari database.
Masalah pada github GraalVM menemukan bug yang terkait dengan driver Postgres, tetapi pada kandidat rilis GraalVM. Semuanya ditandai sebagai sudah diperbaiki.
Saya menghubungkan dependensi postgresql:
<dependency> <groupId>org.postgresql</groupId> <artifactId>postgresql</artifactId> <version>42.2.5</version> </dependency>
Saya menulis kode untuk mengekstraksi data dari database (plat pengguna paling sederhana dibuat):
Startup.java public class Startup { public static void main(String[] args) SQLException { final PGSimpleDataSource ds = new PGSimpleDataSource(); ds.setUrl("jdbc:postgresql://localhost/demo_nativem"); ds.setUser("test"); ds.setPassword("test"); try ( Connection conn = ds.getConnection(); Statement stmt = conn.createStatement(); ResultSet rs = stmt.executeQuery("SELECT * FROM \"public\".\"user\""); ) { while(rs.next()){ System.out.print("ID: " + rs.getLong("id")); System.out.println(", Name: " + rs.getString("name")); } } } }
Saya mengumpulkan biner asli dan segera mendapatkan kesalahan build:
Error: No instances are allowed in the image heap for a class that is initialized or reinitialized at image runtime: org.postgresql.Driver. Try marking this class for build-time initialization with --initialize-at-build-time=org.postgresql.Driver
Faktanya adalah bahwa pembangun aplikasi asli menginisialisasi semua bidang statis selama proses membangun (kecuali ditentukan lain), dan ia melakukan ini dengan memeriksa dependensi kelas. Kode saya tidak merujuk ke org.postgresql.Driver, sehingga kolektor tidak tahu bagaimana menginisialisasi dengan lebih baik (saat membangun, atau kapan aplikasi dimulai) dan menawarkan untuk mendaftarkannya untuk inisialisasi saat membangun. Ini dapat dilakukan dengan menambahkannya ke argumen maven dari plugin native-image-maven-plugin, seperti yang ditunjukkan dalam deskripsi kesalahan. Setelah menambahkan Driver, saya mendapatkan kesalahan yang sama terkait dengan org.postgresql.util.SharedTimer. Sekali lagi, saya mengumpulkan dan menemukan kesalahan build seperti itu:
Error: Class initialization failed: org.postgresql.sspi.SSPIClient
Tidak ada rekomendasi untuk koreksi. Tapi, melihat sumber kelasnya, jelas bahwa itu berkaitan dengan eksekusi kode di bawah Windows. Di Linux, inisialisasi (yang terjadi saat perakitan) gagal dengan kesalahan. Ada peluang untuk menunda inisialisasi pada awal aplikasi: - menginisialisasi-saat-run-time = org.postgresql.sspi.SSPIClient. Inisialisasi di Linux tidak akan terjadi dan kami tidak akan lagi mendapatkan kesalahan terkait dengan kelas ini. Bangun Argumen:
<buildArgs> --no-server --no-fallback --initialize-at-build-time=org.postgresql.Driver --initialize-at-build-time=org.postgresql.util.SharedTimer --initialize-at-run-time=org.postgresql.sspi.SSPIClient </buildArgs>
Perakitan mulai memakan waktu 1 menit 20 detik dan file membengkak hingga 11 MB. Saya menambahkan bendera tambahan untuk membangun biner: --tidak ada fallback yang melarang pembuatan biner asli yang membutuhkan mesin Java yang diinstal. Biner seperti itu dibuat jika kolektor mendeteksi penggunaan fitur bahasa yang tidak didukung di Substrate VM atau memerlukan konfigurasi, tetapi belum ada konfigurasi. Dalam kasus saya, kolektor menemukan potensi penggunaan refleksi pada driver JDBC. Tapi ini hanya penggunaan potensial, tidak diperlukan dalam program saya, dan oleh karena itu, konfigurasi tambahan tidak diperlukan (bagaimana melakukannya akan ditampilkan nanti). Ada juga flag --static, yang memaksa generator untuk menghubungkan libc secara statis. Tetapi jika Anda menggunakannya, program macet dengan kesalahan segmentasi ketika Anda mencoba untuk menyelesaikan nama jaringan ke alamat IP. Saya mencari solusi untuk masalah ini, tetapi tidak menemukan sesuatu yang cocok, jadi saya meninggalkan ketergantungan program pada libc.
Saya menjalankan biner yang dihasilkan dan mendapatkan kesalahan berikut:
Exception in thread "main" org.postgresql.util.PSQLException: Could not find a java cryptographic algorithm: TLS SSLContext not available.
Setelah beberapa penelitian, penyebab kesalahan telah diidentifikasi: Postgres, secara default, membuat koneksi TLS menggunakan Kurva Elliptic. SubstrateVM tidak termasuk implementasi algoritme untuk TLS, berikut adalah masalah terbuka yang sesuai -
Dukungan ECC (ECDSA / ECDHE) biner tunggal untuk SubstrateVM . Ada beberapa solusi: letakkan perpustakaan dari paket GraalVM: libsunec.so di sebelah aplikasi, konfigurasikan daftar algoritma pada server Postgres, kecualikan algoritma Elliptic Curve atau cukup nonaktifkan koneksi TLS pada driver Postgres (opsi ini dipilih):
dataSource.setSslMode(SslMode.DISABLE.value);
Setelah menghilangkan kesalahan membuat koneksi dengan Postgres, saya meluncurkan aplikasi asli, itu berjalan dan menampilkan data dari database.
Tautan repositori:
Github: native-java-postgres-demo .
Kerangka DI dan server web tertanam
Ketika mengembangkan aplikasi Java yang kompleks, mereka biasanya menggunakan semacam kerangka kerja, misalnya, Boot Spring. Tetapi dilihat dari artikel ini oleh
dukungan gambar asli GraalVM , karya Spring Boot pada gambar asli “out of the box” dijanjikan kepada kita hanya di Spring Boot 5.3.
Tetapi ada kerangka kerja
Micronaut yang bagus yang
mengklaim dapat bekerja dalam gambar asli GraalVM . Secara umum, menghubungkan Micronaut ke aplikasi yang akan dirakit dalam biner tidak memerlukan pengaturan khusus atau pemecahan masalah. Memang, banyak pengaturan untuk menggunakan refleksi dan menghubungkan sumber daya untuk Substrat VM sudah dibuat di dalam Micronaut. Ngomong-ngomong, pengaturan yang sama dapat ditempatkan di dalam aplikasi Anda dalam file pengaturan META-INF / native-image / $ {groupId} / $ {artifactId} /native-image.properties (jalur untuk file pengaturan ini direkomendasikan oleh Substrate VM), inilah tipikal konten file:
native-image.properties Args = \ -H:+ReportUnsupportedElementsAtRuntime \ -H:ResourceConfigurationResources=${.}/resource-config.json \ -H:ReflectionConfigurationResources=${.}/reflect-config.json \ -H:DynamicProxyConfigurationResources=${.}/proxy-config.json \ --initialize-at-build-time=org.postgresql.Driver \ --initialize-at-build-time=org.postgresql.util.SharedTimer \ --initialize-at-run-time=org.postgresql.sspi.SSPIClient
File resource-config.json, reflect-config.json, proxy-config.json berisi pengaturan untuk menghubungkan sumber daya, refleksi dan proxy yang digunakan (Proxy.newProxyInstance). File-file ini dapat dibuat secara manual atau diambil menggunakan agentlib: native-image-agent. Jika menggunakan native-image-agent, Anda perlu menjalankan toples yang biasa (dan bukan biner asli) menggunakan agen:
java -agentlib:native-image-agent=config-output-dir=output -jar my.jar
di mana output adalah direktori tempat file yang dijelaskan di atas akan ditempatkan. Dalam hal ini, program tidak hanya perlu dijalankan, tetapi juga untuk mengeksekusi skrip dalam program, karena pengaturan ditulis ke file saat Anda menggunakan refleksi, sumber terbuka, membuat proxy. File-file ini dapat ditempatkan META-INF / native-image / $ {groupId} / $ {artifactId} dan direferensikan di native-image.properties.
Saya memutuskan untuk menghubungkan logging menggunakan logback: Saya menambahkan dependensi ke perpustakaan logback-klasik dan file logback.xml. Setelah itu, saya menyusun toples biasa dan menjalankannya menggunakan agen gambar-asli. Ketika program berakhir, pengaturan yang diperlukan file. Jika Anda melihat isinya, Anda dapat melihat bahwa agen tersebut mendaftarkan penggunaan logback.xml untuk dikompilasi ke dalam biner. Juga, file reflection-config.json berisi semua kasus menggunakan refleksi: untuk kelas yang diberikan, informasi meta akan masuk ke dalam biner.
Kemudian saya menambahkan dependensi ke perpustakaan micronaut-http-server-netty untuk menggunakan server web tertanam berbasis netty dan membuat pengontrol:
Startup.java @Controller("/hello") public class HelloController { @Get("/{name}") @Produces(MediaType.TEXT_PLAIN) public HttpResponse<String> hello(String name) { return HttpResponse.ok("Hello " + name); } }
Dan kelas utama:
HelloController.java public class Startup { public static void main(String[] args) { Signal.handle(new Signal("INT"), sig -> System.exit(0)); Micronaut.run(Startup.class, args); } }
Sekarang Anda dapat mencoba membangun biner asli. Pertemuan saya memakan waktu 4 menit. Jika Anda menjalankannya dan pergi ke alamat
http: // localhost: 8080 / halo / pengguna maka kesalahan terjadi:
{"_links":{"self":{"href":"/hello/user","templated":false}},"message":"More than 1 route matched the incoming request. The following routes matched /hello/user: GET - /hello/user, GET - /hello/user"}
Jujur, tidak sepenuhnya jelas mengapa ini terjadi, tetapi setelah mencari dengan mengetik, saya menemukan bahwa kesalahan itu hilang jika baris berikut dihapus dari file resource-config.json (yang dibuat oleh agen):
{"pattern":"META-INF/services/com.fasterxml.jackson.databind.Module"}, {"pattern":"META-INF/services/io.micronaut.context.env.PropertySourceLoader"}, {"pattern":"META-INF/services/io.micronaut.http.HttpResponseFactory"}, {"pattern":"META-INF/services/io.micronaut.inject.BeanConfiguration"}, {"pattern":"META-INF/services/io.micronaut.inject.BeanDefinitionReference"},
Micronaut mendaftarkan sumber daya ini dan tampaknya pendaftaran ulang menyebabkan pendaftaran ganda pada pengontrol saya dan kesalahan. Jika setelah mengoreksi file, Anda membangun kembali biner dan menjalankannya, tidak akan ada lagi kesalahan, teks "Hello user" akan ditampilkan di
http: // localhost: 8080 / hello / user .
Saya ingin menarik perhatian pada penggunaan baris berikut di kelas utama:
Signal.handle(new Signal("INT"), sig -> System.exit(0));
Itu harus dimasukkan agar Micronaut selesai dengan benar. Terlepas dari kenyataan bahwa Micronaut hang hook untuk dimatikan, itu tidak berfungsi dalam biner asli. Ada masalah terkait:
Shutdownhook tidak diaktifkan dengan asli . Itu ditandai sebagai tetap, tetapi, pada kenyataannya, itu hanya memiliki solusi menggunakan kelas Signal.
Tautan repositori:
Github: native-java-postgres-micronaut-demo .
Koneksi ORM
JDBC baik, tetapi ban kode berulang, SELECT dan UPDATE tak berujung. Saya akan mencoba memfasilitasi (atau memperumit, tergantung dari sisi mana yang harus dilihat) hidup saya dengan menghubungkan semacam ORM.
Hibernasi
Pada awalnya saya memutuskan untuk mencoba
Hibernate , karena ini adalah salah satu ORM yang paling umum untuk Java. Tapi saya tidak berhasil membangun gambar asli menggunakan Hibernate karena kesalahan build:
Error: Field java.lang.reflect.Method.defaultValue is not present on type java.lang.reflect.Constructor. Error encountered while analysing java.lang.reflect.Method.getDefaultValue() Parsing context: parsing org.hibernate.annotations.common.annotationfactory.AnnotationProxy.getAnnotationValues(AnnotationProxy.java:63) parsing org.hibernate.annotations.common.annotationfactory.AnnotationProxy(AnnotationProxy.java:52) ...
Ada masalah terbuka yang sesuai:
[native-image] Micronaut + Hibernate menghasilkan Galat yang ditemui saat menganalisis java.lang.reflect.Method.getDefaultValue () .
JOOQ
Kemudian saya memutuskan untuk mencoba
JOOQ . Saya berhasil membangun biner asli, meskipun saya harus membuat banyak pengaturan: menentukan kelas mana yang akan diinisialisasi (buildtime, runtime) dan mengacaukan refleksi. Pada akhirnya, semuanya bermuara pada kenyataan bahwa ketika aplikasi dimulai, jOOQ menginisialisasi proxy org.jooq.impl.ParserImpl $ Abaikan sebagai anggota statis dari kelas org.jooq.impl.Tools. Dan proksi ini menggunakan MethodHandle, yang
belum didukung oleh Substrat VM. Inilah masalah terbuka yang serupa:
[gambar-asli] Micronaut + Kafka gagal membangun gambar asli dengan argumen MethodHandle tidak dapat direduksi menjadi paling banyak satu panggilan .
Cabe Apache
Apache Cayenne kurang umum, tetapi terlihat cukup fungsional. Saya akan mencoba menghubungkannya. Saya membuat file XML untuk menjelaskan skema database, mereka dapat dibuat secara manual atau menggunakan alat GUI CayenneModeler, atau berdasarkan pada database yang ada. Menggunakan cayenne-maven-plugin dalam file pom, pembuatan kode kelas yang sesuai dengan tabel database akan dilakukan:
cayenne-maven-plugin <plugin> <groupId>org.apache.cayenne.plugins</groupId> <artifactId>cayenne-maven-plugin</artifactId> <version>${cayenne.version}</version> <configuration> <map>src/main/resources/db/datamap.map.xml</map> <destDir>${project.build.directory}/generated-sources/cayenne</destDir> </configuration> <executions> <execution> <goals> <goal>cgen</goal> </goals> </execution> </executions> </plugin>
Kemudian saya menambahkan kelas CayenneRuntimeFactory untuk menginisialisasi pabrik konteks database:
CayenneRuntimeFactory.java @Factory public class CayenneRuntimeFactory { private final DataSource dataSource; public CayenneRuntimeFactory(DataSource dataSource) { this.dataSource = dataSource; } @Bean @Singleton public ServerRuntime cayenneRuntime() { return ServerRuntime.builder() .dataSource(dataSource) .addConfig("db/cayenne-test.xml") .build(); } }
HelloController Controller:
HelloController.java @Controller("/hello") public class HelloController { private final ServerRuntime cayenneRuntime; public HelloController(ServerRuntime cayenneRuntime) { this.cayenneRuntime = cayenneRuntime; } @Get("/{name}") @Produces(MediaType.TEXT_PLAIN) public HttpResponse<String> hello(String name) { final ObjectContext context = cayenneRuntime.newContext(); final List<User> result = ObjectSelect.query(User.class).select(context); if (result.size() > 0) { result.get(0).setName(name); } context.commitChanges(); return HttpResponse.ok(result.stream() .map(x -> MessageFormat.format("{0}.{1}", x.getObjectId(), x.getName())) .collect(Collectors.joining(","))); } }
Kemudian ia meluncurkan program sebagai toples biasa, menggunakan agentlib: native-image-agent, untuk mengumpulkan informasi tentang sumber daya yang digunakan dan refleksi.
Saya mengumpulkan biner asli, menjalankannya, pergi ke alamat
http: // localhost: 8080 / halo / pengguna dan mendapatkan kesalahan:
{"message":"Internal Server Error: Provider com.sun.org.apache.xerces.internal.jaxp.SAXParserFactoryImpl not found"}
ternyata agentlib: agen-gambar-asli tidak mendeteksi penggunaan kelas ini dalam refleksi.
Menambahkannya secara manual ke file reflect-config.json:
{ "name":"com.sun.org.apache.xerces.internal.jaxp.SAXParserFactoryImpl", "allDeclaredConstructors":true }
Sekali lagi, saya mengumpulkan biner, memulai, memperbarui halaman web dan mendapatkan kesalahan lain:
Caused by: java.util.MissingResourceException: Resource bundle not found org.apache.cayenne.cayenne-strings. Register the resource bundle using the option -H:IncludeResourceBundles=org.apache.cayenne.cayenne-strings.
Semuanya jelas di sini, saya menambahkan pengaturan, seperti yang ditunjukkan dalam solusi yang diusulkan. Sekali lagi saya mengumpulkan biner (ini adalah 5 menit), saya memulai lagi dan lagi kesalahan, yang lain:
No DataMap found, can't route query org.apache.cayenne.query.SelectQuery@2af96966[root=class name.voyachek.demos.nativemcp.db.User,name=]"}
Saya harus mengotak-atik kesalahan ini, setelah banyak tes, mempelajari sumber-sumbernya, menjadi jelas bahwa alasan untuk kesalahan terletak pada baris ini dari kelas org.apache.cayenne.resource.URLRumberdaya:
return new URLResource(new URL(url, relativePath));
Ternyata, Substrate VM memuat sumber daya dengan url, yang diindikasikan sebagai basis, dan bukan oleh url, yang harus dibentuk berdasarkan basis dan relatifPath. Tentang masalah apa yang didaftarkan oleh saya tentang:
Konten sumber daya tidak valid saat menggunakan URL baru (konteks URL, spesifikasi String) .
Kesalahan ditentukan, sekarang Anda perlu mencari solusi. Untungnya, Apache Cayenne ternyata menjadi hal yang dapat dikustomisasi. Anda harus mendaftarkan pemuat sumber daya Anda sendiri:
ServerRuntime.builder() .dataSource(dataSource) .addConfig("db/cayenne-test.xml") .addModule(binder -> { binder.bind(ResourceLocator.class).to(ClassLoaderResourceLocatorFix.class); binder.bind(Key.get(ResourceLocator.class, Constants.SERVER_RESOURCE_LOCATOR)).to(ClassLoaderResourceLocatorFix.class); }) .build();
Ini kodenya:
ClassLoaderResourceLocatorFix.java public class ClassLoaderResourceLocatorFix implements ResourceLocator { private ClassLoaderManager classLoaderManager; public ClassLoaderResourceLocatorFix(@Inject ClassLoaderManager classLoaderManager) { this.classLoaderManager = classLoaderManager; } @Override public Collection<Resource> findResources(String name) { final Collection<Resource> resources = new ArrayList<>(3); final Enumeration<URL> urls; try { urls = classLoaderManager.getClassLoader(name).getResources(name); } catch (IOException e) { throw new ConfigurationException("Error getting resources for "); } while (urls.hasMoreElements()) { resources.add(new URLResourceFix(urls.nextElement())); } return resources; } private class URLResourceFix extends URLResource { URLResourceFix(URL url) { super(url); } @Override public Resource getRelativeResource(String relativePath) { try { String url = getURL().toString(); url = url.substring(0, url.lastIndexOf("/") + 1) + relativePath; return new URLResource(new URI(url).toURL()); } catch (MalformedURLException | URISyntaxException e) { throw new CayenneRuntimeException( "Error creating relative resource '%s' : '%s'", e, getURL(), relativePath); } } } }
Ini memiliki garis
return new URLResource(new URL(url, relativePath));
diganti oleh:
String url = getURL().toString(); url = url.substring(0, url.lastIndexOf("/") + 1) + relativePath; return new URLResource(new URI(url).toURL());
Saya mengumpulkan biner (70 MB), mulai, buka
http: // localhost: 8080 / halo / pengguna dan semuanya berfungsi, data dari database ditampilkan di halaman.
Tautan repositori:
Github: native-micronaut-cayenne-demo .
Kesimpulan
Tujuannya telah tercapai: aplikasi web sederhana dengan akses ke database menggunakan ORM telah dikembangkan. Aplikasi ini dikompilasi ke dalam binary asli dan dapat berjalan pada sistem tanpa mesin Java diinstal. Meskipun banyak masalah, saya menemukan kombinasi kerangka kerja, pengaturan, dan solusi yang memungkinkan saya untuk mendapatkan program kerja.
Ya, kemampuan untuk membangun binari reguler dari kode sumber Java masih dalam status percobaan. Ini terbukti dengan banyaknya masalah, kebutuhan untuk mencari solusi. Namun pada akhirnya, itu masih ternyata mencapai hasil yang diinginkan. Apa yang saya dapatkan?
- Satu-satunya file mandiri (hampir, ada ketergantungan pada perpustakaan seperti libc) yang dapat berjalan pada sistem tanpa mesin Java.
- Waktu mulai rata-rata 40 milidetik versus 2 detik saat memulai botol biasa.
Di antara kekurangannya, saya ingin mencatat waktu kompilasi panjang dari biner asli. Saya membutuhkan rata-rata lima menit, dan kemungkinan besar akan meningkat ketika menulis kode dan menghubungkan perpustakaan. Oleh karena itu, masuk akal untuk membuat binari berdasarkan kode yang sepenuhnya didebug. Selain itu, informasi debug untuk binari asli hanya tersedia dalam edisi komersial Graal VM - Enterprise Edition.