Sejak bekerja, semakin banyak kode telah ditulis, program ini menjadi semakin kembung, dan efisiensinya menjadi semakin sedikit. Ini sama sekali tidak diizinkan untuk seorang programmer seperti saya yang mengejar kesempurnaan. Oleh karena itu, selain terus mengoptimalkan struktur program, optimasi memori dan penyetelan kinerja telah menjadi "trik" saya yang biasa.
Untuk mengoptimalkan dan menyetel program dan kinerja program Java, jelas tidak mungkin untuk tidak memahami prinsip -prinsip internal mesin virtual (atau spesifikasi yang lebih ketat). Berikut adalah buku yang bagus "Machine Virtual Java (Edisi Kedua)" (oleh Bill Venners, yang diterjemahkan oleh Cao Xiaogang dan Jiang Jing. Faktanya, artikel ini adalah pemahaman pribadi penulis tentang mesin virtual Java setelah membaca buku ini). Tentu saja, manfaat memahami mesin virtual Java tidak terbatas pada dua manfaat di atas. Dari perspektif teknis yang lebih dalam, memahami spesifikasi dan implementasi mesin virtual Java akan lebih membantu bagi kita untuk menulis kode Java yang efisien dan stabil. For example, if we understand the memory model of the Java virtual machine and the memory recycling mechanism of the virtual machine, we will not rely too much on it, but will explicitly "release memory" when needed (Java code cannot explicitly release memory, but we can inform the garbage collector that the object needs to be recycled by releasing the object reference), so as to reduce unnecessary memory consumption; Jika kita memahami cara kerja tumpukan java, kita dapat mengurangi risiko overflow stack dengan mengurangi jumlah lapisan rekursif dan jumlah loop. Untuk pengembang aplikasi, mereka mungkin tidak secara langsung melibatkan pekerjaan implementasi yang mendasari mesin virtual Java ini, tetapi memahami pengetahuan latar belakang ini akan kurang lebih memiliki dampak yang halus dan baik pada program yang kami tulis.
Artikel ini akan menjelaskan secara singkat arsitektur dan model memori mesin virtual Java. Jika ada kata -kata yang tidak pantas atau penjelasan yang tidak akurat, pastikan untuk memperbaikinya. Saya sangat terhormat!
Arsitektur Mesin Virtual Java
Subsistem pemuatan kelas
Ada dua loader kelas untuk mesin virtual Java, yaitu pemuat kelas startup dan loader yang ditentukan pengguna.
Kelas memuat kelas memuat kelas ke area data runtime melalui nama kelas yang sepenuhnya memenuhi syarat (nama paket dan nama kelas, jaringan mount juga termasuk URL). Untuk setiap jenis yang dimuat, mesin virtual Java membuat contoh dari kelas java.lang.class untuk mewakili jenis, yang ditempatkan di area heap dalam memori, dan informasi tipe yang dimuat terletak di area metode, yang sama dengan semua objek lainnya.
Sebelum memuat jenis, subsistem pemuatan kelas tidak hanya harus menemukan dan mengimpor file kelas biner yang sesuai, tetapi juga memverifikasi kebenaran kelas yang diimpor, mengalokasikan dan menginisialisasi memori untuk variabel kelas, dan referensi simbol parse sebagai referensi langsung. Tindakan ini secara ketat dalam urutan berikut:
1) memuat - temukan dan memuat data biner dari tipe;
2) Koneksi - Lakukan Verifikasi, Persiapan dan Parsing (Opsional)
3) Verifikasi untuk memastikan kebenaran jenis yang diimpor
4) Bersiaplah untuk mengalokasikan memori untuk variabel kelas dan menginisialisasi mereka ke nilai default
5) Menganalisis referensi simbolik dalam jenis aplikasi langsung
Area metode
Untuk setiap jenis yang dimuat oleh subsistem pemuatan kelas, mesin virtual menyimpan data berikut ke area metode:
1. Nama jenis yang memenuhi syarat
2. Nama yang sepenuhnya memenuhi syarat dari tipe superclass (java.lang.Object tidak memiliki superclass)
3. adalah tipe tipe kelas A atau tipe antarmuka
4. Ketik pengubah akses
5. Nama yang sepenuhnya memenuhi syarat Daftar yang dipesan dari HyperInterface langsung
Selain informasi tipe dasar di atas, informasi berikut juga akan disimpan:
6. Kolam Konstan Ketik
7. Informasi bidang (termasuk nama bidang, jenis bidang, pengubah bidang)
8. Informasi metode (termasuk nama metode, jenis pengembalian, angka dan jenis parameter, pengubah metode. Jika metode ini tidak abstrak dan lokal, metode bytecode, tumpukan operan dan ukuran dan tabel pengecualian area variabel lokal dalam bingkai stack metode juga akan disimpan)
9. Semua variabel kelas kecuali konstanta (sebenarnya, mereka adalah variabel statis kelas. Karena variabel statis dibagikan oleh semua contoh dan secara langsung terkait dengan jenisnya, mereka adalah variabel tingkat kelas dan disimpan dalam area metode sebagai anggota kelas)
10. Referensi ke ClassLoader
// yang dikembalikan adalah classloader referensi string.class.getClassLoader () yang baru saja disimpan; referensi ke kelas kelas // Ini akan mengembalikan string referensi. Kelas kelas kelas baru saja disimpan sekarang;
Perhatikan bahwa area metode juga dapat didaur ulang oleh pengumpul sampah.
tumpukan
Semua instance atau array kelas yang dibuat oleh program Java saat runtime ditempatkan di tumpukan yang sama, dan setiap mesin virtual Java juga memiliki ruang tumpukan, dan semua utas berbagi tumpukan (inilah sebabnya program Java multi-threaded akan menyebabkan masalah sinkronisasi dalam akses objek).
Karena setiap mesin virtual Java memiliki implementasi yang berbeda dari spesifikasi mesin virtual, kita mungkin tidak tahu bentuk apa masing -masing mesin virtual Java mewakili instance objek dalam tumpukan, tetapi kita bisa melihat sekilas melalui implementasi berikut:
Penghitung program
Untuk menjalankan program Java, setiap utas memiliki register PC (Program Counter) sendiri, yang dibuat ketika utas dimulai, dengan ukuran satu kata, dan digunakan untuk menyimpan lokasi baris kode berikutnya yang perlu dieksekusi.
Java Stack
Setiap utas memiliki tumpukan Java, yang menghemat keadaan utas yang berjalan dalam satuan bingkai tumpukan. Ada dua jenis operasi mesin virtual di Java Stack: Stack menekan dan menumpuk, yang keduanya memiliki bingkai. Bingkai tumpukan menyimpan data seperti parameter yang masuk, variabel lokal, hasil operasi perantara, dll., Yang muncul ketika metode selesai dan kemudian dirilis.
Lihatlah snapshot memori dari bingkai tumpukan saat dua variabel lokal ditambahkan bersama -sama
Tumpukan metode lokal
Di sinilah Java memanggil perpustakaan Sistem Operasi Lokal, yang digunakan untuk mengimplementasikan JNI (Java Native Interface, Java Local Interface)
Mesin eksekusi
Inti dari kontrol mesin virtual Java memuat java bytecode dan parsing; Untuk menjalankan program Java, setiap utas adalah instance dari mesin eksekusi mesin virtual independen. Dari awal hingga akhir siklus hidup utas, ia menjalankan bytecode atau menjalankan metode lokal.
Antarmuka lokal
Terhubung ke Metode Lokal Tumpukan dan Perpustakaan Sistem Operasi.
Catatan: Semua tempat yang disebutkan dalam artikel merujuk pada "Spesifikasi Mesin Virtual Java untuk platform Javaee dan Javase".
Praktek Optimalisasi Memori Mesin Virtual
Karena memori disebutkan, kebocoran memori harus disebutkan. Seperti yang kita semua tahu, Java berkembang dari dasar C ++, dan masalah besar dengan program C ++ adalah bahwa kebocoran memori sulit dipecahkan. Meskipun JVM Java memiliki mekanisme pengumpulan sampah sendiri untuk mendaur ulang memori, dalam banyak kasus, pengembang program Java tidak perlu terlalu khawatir, tetapi ada juga masalah bocor, yang hanya sedikit lebih kecil dari C ++. Misalnya, ada objek yang direferensikan tetapi tidak berguna dalam program: Jika program merujuk objek, tetapi tidak akan atau tidak dapat menggunakannya di masa depan, maka ruang memori yang dibutuhkannya terbuang.
Pertama -tama mari kita lihat cara kerja GC: Pantau status berjalan dari setiap objek, termasuk aplikasi, kutipan, kutipan, penugasan, dll. Ketika objek tidak lagi dikutip, lepaskan objek (fokus GC artikel ini tidak akan dijelaskan terlalu banyak). Banyak programmer Java terlalu banyak mengandalkan GC, tetapi kunci masalahnya adalah bahwa tidak peduli seberapa baik mekanisme pengumpulan sampah JVM, memori selalu merupakan sumber daya yang terbatas. Oleh karena itu, bahkan jika GC akan menyelesaikan sebagian besar pengumpulan sampah untuk kami, masih perlu untuk memperhatikan optimasi memori selama proses pengkodean dengan tepat. Ini dapat secara efektif mengurangi jumlah GC, sambil meningkatkan pemanfaatan memori, dan memaksimalkan efisiensi program.
Secara keseluruhan, optimalisasi memori mesin virtual Java harus dimulai dari dua aspek: mesin virtual Java dan aplikasi Java. Yang pertama mengacu pada mengendalikan ukuran partisi memori logis mesin virtual melalui parameter mesin virtual sesuai dengan desain aplikasi sehingga memori mesin virtual melengkapi persyaratan memori program; Yang terakhir mengacu pada algoritma program yang mengoptimalkan, mengurangi beban GC, dan meningkatkan tingkat keberhasilan daur ulang GC.
Parameter untuk mengoptimalkan memori mesin virtual melalui parameter adalah sebagai berikut:
XMS
Ukuran tumpukan awal
Xmx
Java Heap Nilai Maksimum
1mn
Ukuran tumpukan generasi muda
XSS
Ukuran tumpukan untuk setiap utas
Di atas adalah tiga parameter yang lebih umum digunakan, beberapa:
XX: MinHeapFreeratio = 40
Persentase minimum bebas tumpukan setelah GC untuk menghindari ekspansi.
Xx: maxheapFreeratio = 70
Persentase maksimum tumpukan bebas setelah GC untuk menghindari menyusut.
XX: NewRatio = 2
Rasio ukuran generasi baru/lama. [SPARC -Client: 8; x86 -server: 8; x86 -Client: 12.] -Klien: 8 (1.3.1+), x86: 12]
XX: Newsize = 2.125m
Ukuran default generasi baru (dalam byte) [5.0 dan lebih baru: 64 bit VMS diskalakan 30% lebih besar; x86: 1m; x86, 5.0 dan lebih tua: 640k]
Xx: maxnewsize =
Ukuran maksimum generasi baru (dalam byte). Sejak 1.4, MaxNewSize dihitung sebagai fungsi NewRatio.
XX: Survivorratio = 25
Rasio ukuran ruang Eden/Survivor [SPARC dalam 1.3.1: 25; Platform Solaris Lainnya di 5.0 dan Sebelumnya: 32]
XX: Permsize =
Ukuran awal generasi permanen
Xx: maxpermsize = 64m
Ukuran generasi permanen. [5.0 dan lebih baru: 64 bit VMS diskalakan 30% lebih besar; 1.4 AMD64: 96m; 1.3.1 -Client: 32m.]
Apa yang disebutkan di bawah ini untuk meningkatkan pemanfaatan memori dan mengurangi risiko memori dengan mengoptimalkan algoritma program sepenuhnya empiris dan hanya untuk referensi. Jika ada ketidaksesuaian, tolong perbaiki saya, terima kasih!
1. Lepaskan referensi objek yang tidak berguna sesegera mungkin (xx = null;)
Lihatlah sepotong kode:
Daftar Publik <Pagedata> parse (halaman htmlpage) {Daftar <Pagedata> Daftar = null; coba {list valuelist = page.getByXpath (config.getContentXpath ()); if (valuelist == null || valuelist.isempty ()) {daftar kembali; } // Buat objek saat dibutuhkan, simpan memori dan tingkatkan daftar efisiensi = Daftar ArrayList baru <Pagedata> (); Pagedata pagedata = pagedata baru (); StringBuilder value = new StringBuilder (); untuk (int i = 0; i <valuelist.size (); i ++) {htmlelement content = (htmlelement) valuelist.get (i); Domnodelist <Htmlelement> imgs = content.getElementsbyTagname ("img"); if (imgs! = null &&! imgs.isempty ()) {for (htmlelement img: imgs) {coba {htmlimage image = (htmlimage) img; String path = image.getSrCattribute (); String format = path.substring (path.LastIndexOf ("."), Path.length ()); String localpath = "d:/gambar/" + md5helper.md5 (path) .replace ("//", ","). REPLACE ("/", ",") + format; File localfile = file baru (localpath); if (! localfile.exists ()) {localfile.createNewFile (); Image.Saveas (LocalFile); } image.setAttribute ("src", "file: ////" + localpath); localfile = null; gambar = null; img = null; } catch (Exception e) {}} // Objek ini tidak akan digunakan di masa depan. Membersihkan referensi untuk itu setara dengan memberi tahu GC sebelumnya. Objek dapat mendaur ulang imgs = null; } String text = content.asxml (); value.append (teks) .Append ("<br/>"); valuelist = null; konten = null; teks = null; } pagedata.setContent (value.toString ()); pagedata.setcharset (page.getPageEncoding ()); list.add (pagedata); // pagedata = null; tidak berguna karena daftar masih memegang referensi ke objek, dan GC tidak akan mendaur ulang nilai itu = null; // Tidak ada daftar = nol di sini; Karena daftar adalah nilai pengembalian dari metode ini, jika tidak, nilai pengembalian yang Anda dapatkan dari metode ini akan selalu kosong, dan kesalahan semacam ini tidak mudah ditemukan atau dikecualikan} catch (pengecualian e) {} daftar pengembalian; }2. Gunakan tipe data pengumpulan dengan hati -hati, seperti array, pohon, grafik, daftar tertaut dan struktur data lainnya. Struktur data ini lebih rumit untuk didaur ulang untuk GC.
3. Hindari secara eksplisit mengajukan permohonan ruang array. Ketika Anda harus melamar secara eksplisit, cobalah untuk memperkirakan nilainya yang wajar seakurat mungkin.
4. Cobalah untuk menghindari membuat dan menginisialisasi sejumlah besar objek dalam konstruktor default kelas, dan mencegah pemborosan sumber daya memori yang tidak perlu saat memanggil konstruktor kelasnya sendiri.
5. Cobalah untuk menghindari sistem paksa untuk mendaur ulang memori sampah, dan meningkatkan waktu terakhir daur ulang sampah dalam sistem
6. Coba gunakan variabel nilai instan saat mengembangkan aplikasi panggilan metode jarak jauh, kecuali jika penelepon jarak jauh perlu mendapatkan nilai variabel nilai instan.
7. Coba gunakan teknologi pengumpulan objek dalam skenario yang tepat untuk meningkatkan kinerja sistem