
Aplikasi Berbagi Data Kelas (AppCDS) - Fitur JVM untuk mempercepat startup dan menghemat memori. Setelah muncul dalam masa pertumbuhan di HotSpot kembali di JDK 1.5 (2004), untuk waktu yang lama tetap sangat terbatas, dan bahkan sebagian komersial. Hanya dengan OpenJDK 10 (2018) itu tersedia untuk orang biasa, pada saat yang sama memperluas ruang lingkup. Dan Java 13 yang baru-baru ini dirilis mencoba membuat aplikasi ini lebih sederhana.
Gagasan AppCDS adalah untuk "berbagi" kelas sekali dimuat antara instance dari JVM yang sama pada host yang sama. Tampaknya ini seharusnya bagus untuk layanan microser, terutama "broiler" di Spring Boot dengan ribuan kelas pustaka mereka, karena sekarang kelas-kelas ini tidak perlu dimuat (diuraikan dan diverifikasi) pada setiap awal setiap instance JVM, dan mereka tidak akan diduplikasi dalam memori. Ini berarti peluncuran harus menjadi lebih cepat, dan konsumsi memori harus lebih rendah. Luar biasa, bukan?
Semuanya begitu, semuanya begitu. Tetapi jika Anda, odnokhabryanin, dulu percaya bukan pada tanda-tanda bulevar, tetapi dalam jumlah dan contoh tertentu, maka selamat datang di kat - mari kita coba mencari tahu bagaimana itu sebenarnya ...
Alih-alih disclaimer
Sebelum Anda bukan panduan untuk menggunakan AppCDS, tetapi ringkasan hasil studi kecil. Saya tertarik untuk memahami bagaimana fungsi JVM ini berlaku dalam proyek kerja saya, dan saya mencoba untuk mengevaluasinya dari sudut pandang pengembang perusahaan, menguraikan hasil dalam artikel ini. Ini tidak termasuk topik seperti menggunakan AppCDS pada lintasan modul, mengimplementasikan AppCDS pada mesin virtual lainnya (bukan HotSpot), dan seluk-beluk menggunakan wadah. Tetapi ada bagian teoretis untuk menjelajahi topik, serta bagian eksperimental yang ditulis sehingga Anda dapat mengulangi pengalaman itu sendiri. Belum ada hasil yang diterapkan dalam produksi, tetapi siapa tahu besok seperti apa ...
Teori
Pengantar singkat untuk AppCDS
Kenalan dengan topik ini mungkin terjadi pada Anda di beberapa sumber, misalnya:
- dalam sebuah artikel oleh Nikolai Parlog (termasuk roti Java 13, tetapi tanpa Spring Boot)
- dalam laporan dan artikel oleh Volker Simonis (tanpa Java 13, tetapi dengan detail)
- dalam laporan oleh penulis baris ini (tanpa Java 13, tetapi dengan penekanan pada Spring Boot)
Agar tidak terlibat dalam menceritakan kembali, saya hanya akan menyoroti beberapa poin yang penting untuk artikel ini.
Pertama, AppCDS adalah perpanjangan dari fitur CDS yang telah lama muncul di HotSpot, intinya adalah sebagai berikut:

Untuk menghidupkan kedua gagasan itu, Anda perlu melakukan yang berikut (secara umum):
- Dapatkan daftar kelas yang ingin Anda bagikan di antara instance aplikasi
- Gabungkan kelas-kelas ini ke dalam arsip yang sesuai untuk pemetaan memori
- Hubungkan arsip ke setiap instance aplikasi saat startup
Tampaknya algoritma ini hanya 3 langkah - ambil dan lakukan. Tapi di sini beritanya dimulai, segala macam hal.
Yang buruk adalah bahwa dalam kasus terburuk, masing-masing item ini berubah menjadi setidaknya satu peluncuran JVM dengan opsi spesifiknya sendiri, yang berarti bahwa keseluruhan algoritma adalah juggling halus dari jenis opsi dan file yang sama. Kedengarannya tidak terlalu menjanjikan, bukan?
Tetapi ada kabar baik: bekerja untuk meningkatkan algoritma ini sedang berlangsung , dan dengan setiap rilis Java, aplikasinya menjadi lebih mudah. Jadi misalnya:
- Di OpenJDK 10 dan 11, Anda dapat melewati langkah 1 jika Anda ingin hanya membagikan kelas JDK utama, karena mereka telah dikompilasi untuk kami dan dimasukkan ke dalam
$JAVA_HOME\lib\classlist
(โ1200 pcs.). - Di OpenJDK 12, Anda dapat melewati langkah 2 , karena bersama dengan daftar kelas, arsip distribusi juga menyertakan arsip yang sudah jadi, yang digunakan di luar kotak dan tidak memerlukan koneksi eksplisit.
- Jika Anda ingin membagikan yang lainnya (dan biasanya hanya ingin)
OpenJDK 13 menyediakan Arsip CDS Dinamis - arsip yang dikumpulkan selama pengoperasian aplikasi dan disimpan ketika dikelola. Ini memungkinkan Anda untuk mengurai poin 1 dan 2 menjadi satu titik yang tidak terlalu membingungkan (walaupun tidak semuanya begitu sederhana, tetapi lebih lanjut tentang itu nanti)
Jadi, tidak peduli apa proses mempersiapkan AppCDS, 3 langkah yang tercantum di atas selalu di belakangnya, hanya dalam beberapa kasus mereka terselubung.
Seperti yang mungkin Anda perhatikan, dengan munculnya AppCDS, banyak kelas aplikasi memulai kehidupan ganda: mereka hidup secara simultan di tempat sebelumnya (paling sering file JAR) dan di arsip bersama yang baru. Pada saat yang sama, pengembang terus mengubah / menghapus / menambah mereka di tempat yang sama, dan JVM mengambilnya dari yang baru saat bekerja. Seseorang tidak perlu menjadi peramal untuk melihat bahaya dari situasi seperti itu: jika tidak ada yang dilakukan, cepat atau lambat salinan kelas akan menimbulkan korosi, dan kita akan mendapatkan banyak pesona dari "JAR neraka" yang khas. Jelas bahwa JVM tidak dapat mencegah perubahan kelas, tetapi harus dapat mendeteksi perbedaan waktu. Namun, untuk melakukan ini dengan membandingkan kelas secara berpasangan, bahkan dengan checksum, adalah sebuah ide; itu dapat meniadakan sisa keuntungan produktivitas. Ini mungkin mengapa insinyur JVM tidak memilih kelas individu sebagai objek perbandingan, tetapi seluruh classpath, dan dinyatakan dalam dokumentasi AppCDS: "Jalan setapak saat membuat arsip bersama harus sama (atau setidaknya awalan) seperti dengan peluncuran aplikasi berikutnya."
Perhatikan bahwa classpath yang digunakan pada waktu pembuatan arsip harus sama dengan (atau awalan) classpath yang digunakan saat run time.
Tapi ini bukan pernyataan yang jelas, karena, seperti yang Anda ingat, classpath dapat dibentuk dengan cara yang berbeda, seperti:
- membaca file
.class
kosong dari direktori paket yang dikompilasi,
mis. java com.example.Main
- memindai direktori dengan file JAR saat menggunakan wildcard,
mis. java -cp mydir/* com.example.Main
- daftar eksplisit file JAR dan / atau ZIP,
mis. java -cp lib1.jar;lib2.jar com.example.Main
(ini tidak termasuk fakta bahwa classpath juga dapat diatur secara berbeda, misalnya, melalui opsi JVM -cp/-classpath/--class-path
, variabel lingkungan CLASSPATH
atau atribut file JAR Class-Path
akan diluncurkan)
Dari metode ini, hanya satu yang didukung di AppCDS - enumerasi eksplisit file JAR. Rupanya, para insinyur JVM HotSpot merasa bahwa membandingkan jalur kelas dalam arsip AppCDS dan dalam aplikasi yang diluncurkan akan cukup cepat dan dapat diandalkan hanya jika mereka ditentukan sejelas mungkin - dengan daftar lengkap yang biasa.
CDS / AppCDS mendukung kelas pengarsipan hanya dari file JAR.
Penting untuk dicatat di sini bahwa pernyataan ini tidak berulang, mis. tidak berlaku untuk file JAR di dalam file JAR (kecuali jika itu tentang CD Dinamis, lihat di bawah). Ini berarti bahwa boneka JAR biasa yang dikeluarkan oleh Spring Boot tidak akan berfungsi begitu saja dengan AppCDS biasa, Anda harus duduk.
Tangkapan lain dalam karya CDS adalah bahwa arsip bersama diproyeksikan ke memori dengan alamat tetap (biasanya mulai dari 0x800000000
). Ini sendiri tidak buruk, tetapi karena Address Space Layout Randomization (ASLR) diaktifkan secara default pada sebagian besar sistem operasi, rentang memori yang diperlukan mungkin terisi sebagian. Apa yang dilakukan HotSpot JVM dalam hal ini adalah opsi khusus -Xshare
yang mendukung tiga nilai:
-Xshare:on
- force CDS / AppCDS; jika rentang sibuk, JVM keluar dengan kesalahan. Mode ini tidak disarankan untuk digunakan dalam produksi , karena ini dapat menyebabkan crash sporadis ketika meluncurkan aplikasi.-Xshare:off
- (Anda) beralih CDS / AppCDS; menonaktifkan penggunaan data bersama sepenuhnya (termasuk arsip tertanam)-Xshare:auto
- perilaku default JVM ketika itu, dalam kasus ketidakmungkinan untuk mengalokasikan rentang memori yang diperlukan, menyerah dengan tenang dan memuat kelas seperti biasa
Pada saat penulisan artikel ini, Oracle sedang berupaya untuk mengatasi masalah tersebut, tetapi nomor rilis belum ditetapkan.
Opsi ini sebagian berguna bagi kita nanti, tetapi untuk sekarang mari kita lihat ...
Aplikasi AppCDS
Ada beberapa cara yang Anda bisa dengan AppCDS. merusak hidupmu mengoptimalkan kerja layanan microser. Mereka sangat bervariasi dalam kompleksitas dan potensi keuntungan, jadi penting untuk segera memutuskan mana yang akan dibahas nanti.
Yang paling sederhana adalah menggunakan bahkan AppCDS, tetapi hanya CDS - ini adalah ketika hanya kelas platform masuk ke arsip bersama (lihat "Pengantar Singkat untuk AppCDS"). Kami akan segera menghapus opsi ini, karena ketika diterapkan ke layanan microser di Boot Musim Semi itu memberi terlalu sedikit keuntungan. Ini dapat dilihat dengan proporsi jumlah kelas bersama dalam distribusi umum mereka menggunakan contoh dari satu layanan nyata mikro (lihat segmen hijau):

Lebih kompleks, tetapi menjanjikan adalah penggunaan AppCDS lengkap, yaitu, dimasukkannya kedua perpustakaan dan kelas aplikasi dalam arsip yang sama. Ini adalah seluruh keluarga opsi yang berasal dari kombinasi jumlah aplikasi yang berpartisipasi dan jumlah instance. Berikut ini adalah penilaian penulis subyektif tentang manfaat dan kesulitan berbagai aplikasi AppCDS.
Perhatikan:
- Dalam aplikasi ke satu aplikasi dalam satu contoh (No. 1), laba memori dapat berubah menjadi nol atau bahkan negatif (terutama ketika mengukur di bawah Windows )
- Membuat arsip bersama yang benar memerlukan tindakan, kerumitannya tidak tergantung pada berapa banyak salinan yang akan diluncurkan aplikasi (bandingkan pasangan opsi No. 1-2 dan No. 3-4)
- Pada saat yang sama, transisi dari satu contoh ke beberapa, jelas, memberikan peningkatan laba untuk kedua indikator, tetapi tidak mempengaruhi kompleksitas persiapan.
Dalam artikel ini, kita hanya akan mencapai opsi No. 2 (melalui No. 1), karena cukup sederhana untuk berkenalan dekat dengan AppCDS dan hanya untuk itu tanpa trik tambahan kita dapat menggunakan Arsip CDS Dinamis JEP-350 yang baru dirilis, yang ingin saya rasakan saat beraksi.
Arsip CDS dinamis
JEP-350 Dynamic CDS Archives, salah satu inovasi utama Java 13, dirancang untuk menyederhanakan penggunaan AppCDS. Untuk merasakan penyederhanaan, Anda harus terlebih dahulu memahami kompleksitasnya. Biarkan saya mengingatkan Anda bahwa algoritma aplikasi "bersih" klasik untuk AppCDS terdiri dari 3 langkah: (1) dapatkan daftar kelas bersama, (2) buat arsip dari mereka, dan (3) jalankan aplikasi dengan arsip terhubung. Dari langkah-langkah ini, hanya yang ke-3 yang benar-benar berguna, sisanya hanya persiapan untuk itu. Dan walaupun mendapatkan daftar kelas (langkah # 1) mungkin tampak sangat sederhana (dalam beberapa kasus ini bahkan opsional), pada kenyataannya ketika bekerja dengan aplikasi non-sepele, ternyata menjadi yang paling sulit, terutama yang berkaitan dengan Spring Boot. Jadi JEP-350 diperlukan hanya untuk menghilangkan langkah ini, atau lebih tepatnya, mengotomatiskannya. Idenya adalah bahwa JVM sendiri menyusun daftar kelas yang dibutuhkan aplikasi, dan kemudian dengan sendirinya membentuk apa yang disebut "dinamis" arsip dari mereka. Setuju, kedengarannya bagus. Tetapi yang menarik adalah bahwa sekarang menjadi tidak jelas pada titik apa untuk berhenti mengumpulkan kelas dan melanjutkan untuk menempatkannya di arsip. Sebelumnya, di AppCDS klasik, kami memilih momen seperti itu sendiri dan bahkan bisa menyelipkan antara tindakan ini untuk mengubah sesuatu dalam daftar kelas sebelum mengubahnya menjadi arsip. Sekarang ini terjadi secara otomatis dan hanya pada satu saat, di mana para insinyur JVM telah memilih, mungkin, satu-satunya pilihan kompromi - penutupan rutin JVM. Ini berarti bahwa arsip tidak akan dibuat sampai aplikasi berhenti. Solusi ini memiliki beberapa konsekuensi penting:
- Jika terjadi crash JVM, arsip tidak akan dibuat, betapapun hebatnya daftar kelas yang terakumulasi saat itu (Anda tidak dapat mengekstraknya nanti menggunakan cara biasa).
- Arsip akan dibuat hanya dari kelas-kelas yang berhasil dimuat selama sesi aplikasi. Untuk aplikasi web, ini berarti membuat arsip dengan memulai dan berhenti di sana tidak benar, karena banyak kelas penting tidak akan masuk ke arsip. Penting untuk menjalankan setidaknya satu permintaan HTTP ke aplikasi (dan lebih baik menjalankannya dengan benar di semua skenario) sehingga semua kelas yang digunakan benar-benar dimuat.
Perbedaan penting antara arsip dinamis dan statis adalah bahwa mereka selalu merupakan "tambahan" atas arsip statis dasar, yang dapat berupa arsip yang dibangun dalam kit distribusi Java atau dibuat secara terpisah dengan cara 3 langkah klasik.
Secara sintaksis, menggunakan Arsip CDS Dinamis diturunkan menjadi dua peluncuran JVM dengan dua opsi:
- Percobaan dijalankan dengan opsi
-XX:ArchiveClassesAtExit=archive.jsa
, di mana akhirnya arsip dinamis akan dibuat (Anda dapat menentukan jalur dan nama apa saja) - Peluncuran yang berguna dengan opsi
-XX:SharedArchiveFile=archive.jsa
, yang akan menggunakan arsip yang dibuat sebelumnya
Opsi kedua tidak berbeda dengan menghubungkan arsip statis biasa. Tetapi jika tiba-tiba arsip statis dasar tidak berada di lokasi default (di dalam JDK), maka opsi ini juga dapat menyertakan indikasi jalur ke sana, misalnya:
-XX:SharedArchiveFile=base.jsa:dynamic.jsa
(di bawah Windows, pemisah jalur harus karakter ";")
Sekarang Anda cukup tahu tentang AppCDS sehingga Anda dapat melihatnya dalam tindakan.
Berlatih
Kelinci percobaan
Agar aplikasi AppCDS kami dalam praktiknya tidak terbatas pada HelloWorld biasa, kami akan mengambil sebagai dasar aplikasi nyata pada Spring Boot. Rekan-rekan saya dan saya sering harus menonton log aplikasi pada server uji jarak jauh, dan menonton "langsung", sama seperti yang tertulis. Untuk menggunakan ini, agregator log lengkap (seperti ELK) seringkali tidak sesuai; mengunduh file log tanpa henti - untuk waktu yang lama, dan melihat keluaran konsol abu-abu yang tertekan sangat menyedihkan. Oleh karena itu, saya membuat aplikasi web yang dapat menampilkan semua log secara real time langsung ke browser, mewarnai garis berdasarkan tingkat kepentingan (pada saat yang sama memformat XML), menggabungkan beberapa log menjadi satu, serta trik lainnya. Itu disebut ANALOG (seperti "log analyzer", meskipun ini tidak benar) dan terletak di GitHub . Klik pada tangkapan layar untuk memperbesar:

Secara teknis, ini adalah aplikasi pada Spring Boot + Spring Integration, di bawah kap yang bekerja tail
, docker
dan kubectl
(untuk mendukung log dari file, wadah Docker dan sumber daya Kubernetes, masing-masing). Muncul dalam bentuk file JAR Spring Boot "tebal" klasik. Dalam runtime, kelas โ10K tergantung pada memori aplikasi, yang sebagian besar adalah kelas Spring dan JDK. Jelas, kelas-kelas ini sangat jarang berubah, yang berarti bahwa mereka dapat dimasukkan ke dalam arsip bersama dan digunakan kembali dalam semua contoh aplikasi, menghemat memori dan CPU.
Eksperimen tunggal
Sekarang mari kita terapkan pengetahuan yang ada tentang Dynamic AppCDS ke kelinci percobaan. Karena semuanya diketahui sebagai perbandingan, kami akan memerlukan beberapa titik referensi - keadaan program yang akan kami bandingkan dengan hasil yang diperoleh selama percobaan.
Kata pengantar
- Semua perintah lebih lanjut adalah untuk Linux. Perbedaan untuk Windows dan macOS tidak mendasar.
- Kompilasi JIT secara nyata dapat mempengaruhi hasil dan, secara teori, untuk kemurnian percobaan, dapat dimatikan (dengan opsi
-Xint
, seperti yang dilakukan dalam artikel yang disebutkan), tetapi demi kredibilitas maksimum diputuskan untuk tidak melakukan ini. - Angka-angka berikut tentang waktu mulai diperoleh pada server uji cepat. Pada mesin yang bekerja, angka yang sama, sebagai suatu peraturan, lebih sederhana, tetapi karena kami tidak tertarik pada nilai absolut, tetapi dalam peningkatan persentase, kami menganggap perbedaan ini tidak signifikan.
- Agar tidak terlalu dini memasuki kompleksitas pengukuran memori bersama, untuk saat ini kita akan menghilangkan pembacaan yang akurat dalam byte. Sebagai gantinya, kami memperkenalkan konsep " potensi CDS, " yang dinyatakan sebagai persentase dari jumlah kelas bersama dengan jumlah total kelas yang dimuat. Ini, tentu saja, adalah kuantitas abstrak, tetapi di sisi lain, secara langsung mempengaruhi konsumsi memori yang sebenarnya; selain itu, definisinya tidak bergantung pada OS sama sekali, dan untuk perhitungannya, hanya log yang cukup.
Titik referensi
Biarkan titik ini menjadi keadaan aplikasi yang baru diunduh, mis. tanpa penggunaan eksplisit dari AppCDS'ov dan lainnya. Untuk mengevaluasinya, kita perlu:
Instal OpenJDK 13 (misalnya, distribusi Liberica domestik, tetapi bukan versi lite).
Itu juga perlu ditambahkan ke variabel lingkungan PATH atau ke JAVA_HOME
, misalnya, seperti ini:
export JAVA_HOME=~/tools/jdk-13
Unduh ANALOG (pada saat penulisan, versi terbaru adalah v0.12.1).
Jika perlu, Anda dapat menentukan dalam file config/application.yaml
di server.address
parameter nama host eksternal untuk mengakses aplikasi (secara default, localhost
ditentukan di sana).
Aktifkan pencatatan beban kelas JVM.
Untuk melakukan ini, Anda dapat JAVA_OPTS
- JAVA_OPTS
variabel lingkungan JAVA_OPTS
dengan nilai ini:
export JAVA_OPTS=-Xlog:class+load=info:file=log/class-load.log
Opsi ini akan diteruskan ke JVM dan memberitahukannya untuk menjaminkan sumber dari setiap kelas.
Jalankan uji coba:
- Jalankan aplikasi dengan skrip
bin/analog
- Buka http: // localhost: 8083 di browser, tekan tombol dan daw
- Hentikan aplikasi dengan menekan
Ctrl+C
di konsol skrip bin/analog
Ambil hasilnya (dari file di log/
direktori)
Total jumlah kelas yang dimuat (berdasarkan class-load.log
):
cat class-load.log | wc -l 10463
Berapa banyak dari mereka yang diunduh dari arsip bersama (sesuai dengan itu):
grep -o 'source: shared' - class-load.log 1146
Waktu mulai rata-rata (setelah serangkaian awal; dengan analog.log
):
grep -oE '\(JVM running for .+\)' analog.log | grep -oE '[0-9]\.[0-9]+' | awk '{ total += $1; count++ } END { print total/count }' 4.5225
Jadi, pada langkah ini, potensi CDS adalah 1146/10463=0,1095
โ11% . Jika Anda terkejut dari mana asal kelas yang dibagi (setelah semua, kami belum memasukkan AppCDS), maka saya mengingatkan Anda bahwa mulai dari versi ke-12, JDK menyertakan arsip CDS yang telah selesai $JAVA_HOME/lib/server/classes.jsa
, dibangun oleh tidak kurang dari daftar kelas siap:
cat $JAVA_HOME/lib/classlist | wc -l 1170
Sekarang, setelah menilai keadaan awal aplikasi, kita dapat menerapkan AppCDS untuk itu dan, sebagai perbandingan, memahami apa yang diberikannya.
Pengalaman inti
Saat dokumentasi mewariskan kepada kami, untuk membuat arsip AppCDS yang dinamis, Anda hanya perlu melakukan satu kali uji coba aplikasi dengan opsi -XX:ArchiveClassesAtExit
. Dari peluncuran berikutnya, arsip dapat digunakan dan menerima keuntungan. Untuk memverifikasi ini pada kelinci percobaan yang sama (AnaLog), Anda perlu:
Tambahkan opsi yang ditentukan untuk menjalankan perintah:
export JAVA_OPTS="$JAVA_OPTS -XX:ArchiveClassesAtExit=work/classes.jsa"
Perpanjang logging:
export JAVA_OPTS="$JAVA_OPTS -Xlog:cds=debug:file=log/cds.log"
Opsi ini akan memaksa proses pembuatan arsip CDS untuk dicatat ketika aplikasi dihentikan.
Lakukan uji coba yang sama seperti dengan titik referensi:
- Jalankan aplikasi dengan skrip
bin/analog
- Buka http: // localhost: 8083 di browser, tekan tombol dan daw
- Hentikan aplikasi dengan menekan
Ctrl+C
di konsol skrip bin/analog
Setelah itu, alas kaki yang luar biasa dengan segala macam peringatan akan jatuh ke konsol, dan file log/cds.log
harus diisi dengan detail; mereka belum menarik minat kita.
Alihkan mode peluncuran dari percobaan ke berguna:
export JAVA_OPTS="-XX:SharedArchiveFile=work/classes.jsa -Xlog:class+load=info:file=log/class-load.log -Xlog:class+path=debug:file=log/class-path.log"
Di sini kita tidak menambah variabel JAVA_OPTS
, tetapi menimpanya dengan nilai-nilai baru yang meliputi (1) menggunakan arsip bersama, (2) sumber kelas logging dan (3) logging jalur kelas logging.
Lakukan peluncuran aplikasi yang bermanfaat sesuai dengan skema dari paragraf 3.
Ambil hasilnya (dari file di log/
direktori)
Memverifikasi bahwa AppCDS benar-benar diterapkan (berdasarkan class-path.log
):
[0.011s][info][class,path] type=BOOT [0.011s][info][class,path] Expecting BOOT path=/home/upc/tools/jdk-13/lib/modules [0.011s][info][class,path] ok [0.011s][info][class,path] type=APP [0.011s][info][class,path] Expecting -Djava.class.path=/home/upc/tmp/analog/lib/analog.jar [0.011s][info][class,path] ok
Tanda ok
setelah type=BOOT
baris type=BOOT
dan type=APP
menunjukkan keberhasilan pembukaan, verifikasi, dan pemuatan arsip CDS bawaan dan yang diterapkan.
Total jumlah kelas yang dimuat (berdasarkan class-load.log
):
cat class-load.log | wc -l 10403
Berapa banyak dari mereka yang diunduh dari arsip bersama (sesuai dengan itu):
grep -o 'source: shared' -c class-load.log 6910
Waktu mulai rata-rata (setelah serangkaian mulai; dengan file analog.log
):
grep -oE '\(JVM running for .+\)' analog.log | grep -oE '[0-9]\.[0-9]+' | awk '{ total += $1; count++ } END { print total/count }' 4.04167
Tetapi pada langkah ini, potensi CDS sudah 6910/10403โ0,66
= 66% , artinya, meningkat sebesar 55% dibandingkan dengan titik referensi. Pada saat yang sama, waktu peluncuran rata-rata berkurang sebesar (4,5225-4,04167)=0,48
detik, mis. mulai lebih cepat dengan โ10.6% dari nilai awal.
Analisis Hasil
Judul pekerjaan dari item tersebut adalah: "Mengapa begitu sedikit?"
Kami, seperti, melakukan semuanya sesuai dengan instruksi, tetapi tidak semua kelas ada di arsip. Jumlah mereka memengaruhi waktu peluncuran tidak kurang dari kekuatan komputasi mesin eksperimen, jadi kami akan berkonsentrasi pada jumlah ini.
Jika Anda ingat, kami mengabaikan file log/cds.log
dibuat saat berhenti dari aplikasi eksperimental setelah percobaan berjalan. Dalam file HotSpot ini, JVM dengan baik hati mencatat dengan peringatan peringatan setiap kelas yang tidak muncul dalam arsip CDS. Berikut adalah jumlah total dari tanda tersebut:
grep -o '[warning]' cds.log -c 3591
Mempertimbangkan bahwa hanya 10K + kelas yang disebutkan dalam log class-load.log
dan 66% dari mereka dimuat dari arsip, tidak sulit untuk memahami bahwa 3600 kelas yang terdaftar di cds.log
adalah "hilang" 44% dari potensi CDS. Sekarang Anda perlu mencari tahu mengapa mereka dilewati.
Jika Anda melihat log cds.log, ternyata hanya ada 4 alasan unik untuk melewatkan kelas. Berikut adalah contoh masing-masing:
Skipping org/springframework/web/client/HttpClientErrorException: Not linked Pre JDK 6 class not supported by CDS: 49.0 org/jrobin/core/RrdUpdater Skipping java/util/stream/Collectors$$Lambda$554: Unsafe anonymous class Skipping ch/qos/logback/classic/LoggerContext: interface org/slf4j/ILoggerFactory is excluded
Di antara semua 3591 kelas yang terlewat, alasan ini ditemukan di sini dengan frekuensi seperti ini:

Lihatlah lebih dekat pada mereka:
Unsafe anonymous class
JVM โโ , -, .
Not linked
, โโ , , . , StackOverflow . , , โโ () JAR- , AppCDS. , ( ).
Pre JDK 6 class
, CDS Java 5. class- , CDS . , , 6, Java, . - , runtime- (, slf4j).
Skipping ... : super class/interface ... is excluded
, โโ . CDS', . Sebagai contoh:
[warning][cds] Pre JDK 6 class not supported by CDS: 49.0 org/slf4j/spi/MDCAdapter [warning][cds] Skipping ch/qos/logback/classic/util/LogbackMDCAdapter: interface org/slf4j/spi/MDCAdapter is excluded
Kesimpulan
CDS 100%.
, , , , , . .
JEP-310 , AppCDS JDK. . , . CDS (, , ) .
Untuk mengkloning kelinci percobaan (jalankan AnaLog dalam beberapa kasus), kita perlu mengubah sesuatu di pengaturan; ini akan memungkinkan proses yang diangkat tidak ke "siku". Berkat Spring Boot, Anda dapat melakukan ini tanpa mengedit atau menyalin file apa pun; pengaturan apa pun dapat ditimpa oleh opsi JVM. Meneruskan opsi-opsi ini dari variabel lingkungan ANALOG_OPTS
menyediakan skrip peluncuran, yang dibuat dengan baik oleh Gradle.
export ANALOG_OPTS="-Djavamelody.enabled=false -Dlogging.config=classpath:logging/logback-console.xml" export ANALOG_OPTS="$ANALOG_OPTS -Dnodes.this.agentPort=7801 -Dserver.port=8091"
JavaMelody, , , . TCP- ; .
, , JVM AppCDS . JAVA_OPTS
JVM Unified Logging Framework :
export JAVA_OPTS="-Xlog:class+load=info:file=log/class-load-%p.log -Xlog:class+path=debug:file=log/class-path-%p.log" export JAVA_OPTS="$JAVA_OPTS -XX:SharedArchiveFile=work/classes.jsa"
%p
, JVM (PID). AppCDS , ( ).
, . . :
server.port
nodes.this.agentPort
, :
export ANALOG_OPTS="$ANALOG_OPTS -Dnodes.this.agentPort=7801 -Dserver.port=8091"
, ( ).
bin/analog
() http://localhost:8091 ,
PID ( ), :
pgrep -f analog 13792
pmap
( ):
pmap -XX 13792 | sed -n -e '2p;$p' Address Perm Offset Device Inode Size KernelPageSize MMUPageSize Rss Pss Shared_Clean Shared_Dirty Private_Clean Private_Dirty Referenced Anonymous LazyFree AnonHugePages ShmemPmdMapped Shared_Hugetlb Private_Hugetlb Swap SwapPss Locked ProtectionKey VmFlagsMapping 3186952 1548 1548 328132 325183 3256 0 10848 314028 212620 314024 0 0 0 0 0 0 0 325183 0 KB
; .
1-4 (, ).
pmap
. CDS' . , , PSS:
The "proportional set size" (PSS) of a process is the count of pages it has in memory, where each page is divided by the number of processes sharing it. So if a process has 1000 pages all to itself, and 1000 shared with one other process, its PSS will be 1500.
, , โ โ . , .
PSS , :
, - :
, . AppCDS. , -XX:SharedArchiveFile=work/classes.jsa
-Xshare:off
, CDS . , .

:
PSS AppCDS CDS.
. , , HelloWorld- JVM CDS 2 , CDS. PSS CDS, . :
PSS AppCDS 2- ; 3- .
, , , . , AppCDS, , , 3- .
: , CDS? :
CDS/AppCDS JVM , PSS . , , pmap
, โโ sed
'. :
pmap -X `pgrep -f analog` 14981:
( Mapping
) , โโ . JVM ( libjvm.so
), ( libc-2.27.so
). :
For the Java VM, the read-only parts of the loaded shared libraries (ie libjvm.so
) can be shared between all the VM instances running at the same time. This explains why, taking together, the two VM's consume less memory (ie have a smaller memory footprint) than the simple sum of their single resident set sizes when running alone.
. , , . , , JVM , Java- . GeekOut:

, , , AppCDS , .. Java-. , JVM, , - .
VisualVM Metaspace AppCDS , :
AppCDS

AppCDS

, 128 Metaspace AppCDS 64.2 MiB / 8.96 MiB
โ7,2 , CDS . (. ) 66.4 MiB / 13.9 MiB
โ4,8 . , AppCDS , Metaspace. Metaspace, , CDS .
Alih-alih sebuah kesimpulan
Spring Boot AppCDS โ JVM, .
- JEP-350 Dynamic CDS Archives โ JDK 13.
- Spring Boot รณ CDS ( ). , 100% - 66% . , โ11% ( 15%, ).
- , 5- PSS ( ). , AppCDS , , 8% (PSS). , CDS, , . AppCDS .
- Metaspace, , AppCDS 5 , CDS.
, , AppCDS, , โkiller featureโ. Spring Boot. , , AppCDS . , , AppCDS Spring Boot. , โฆ
by Nick Fewings on Unsplash