Bongkar kelas di java?

174

Saya memiliki pemuat kelas khusus sehingga aplikasi desktop secara dinamis dapat mulai memuat kelas dari AppServer yang perlu saya ajak bicara. Kami melakukan ini karena jumlah stoples yang diperlukan untuk melakukan ini konyol (jika kami ingin mengirimkannya). Kami juga memiliki masalah versi jika kami tidak memuat kelas secara dinamis saat dijalankan dari pustaka AppServer.

Sekarang, saya baru saja mengalami masalah di mana saya perlu berbicara dengan dua AppServers yang berbeda dan menemukan bahwa tergantung pada kelas mana saya memuat pertama saya mungkin rusak parah ... Apakah ada cara untuk memaksa pembongkaran kelas tanpa benar-benar membunuh JVM?

Semoga ini masuk akal

el_eduardo
sumber
Apakah Anda memiliki classloader untuk setiap toples? Bagaimana cara OSGI mengirim arsip untuk membongkar classloader? Tampaknya tidak ada API yang dibongkar di kelas classloader?
hetaoblog

Jawaban:

190

Satu-satunya cara Kelas dapat dibongkar adalah jika Classloader yang digunakan adalah sampah yang dikumpulkan. Ini berarti, referensi untuk setiap kelas tunggal dan ke classloader itu sendiri harus sesuai dengan yang dodo.

Salah satu solusi yang mungkin untuk masalah Anda adalah memiliki Classloader untuk setiap file jar, dan Classloader untuk masing-masing AppServers yang mendelegasikan pemuatan kelas yang sebenarnya ke classloader Jar tertentu. Dengan begitu, Anda bisa mengarahkan ke berbagai versi file jar untuk setiap server Aplikasi.

Ini bukan hal sepele. Platform OSGi berusaha melakukan ini, karena setiap bundel memiliki classloader yang berbeda dan dependensi diselesaikan oleh platform. Mungkin solusi yang baik adalah dengan melihatnya.

Jika Anda tidak ingin menggunakan OSGI, satu kemungkinan implementasi bisa menggunakan satu instance dari JarClassloader kelas untuk setiap file JAR.

Dan buat kelas MultiClassloader baru yang memperluas Classloader. Kelas ini secara internal akan memiliki array (atau Daftar) dari JarClassloaders, dan dalam metode defineClass () akan beralih melalui semua classloader internal sampai definisi dapat ditemukan, atau NoClassDefFoundException dilemparkan. Beberapa metode pengakses dapat disediakan untuk menambahkan JarClassloader baru ke kelas. Ada beberapa kemungkinan implementasi di internet untuk MultiClassLoader, jadi Anda bahkan mungkin tidak perlu menulis sendiri.

Jika Anda menginstal MultiClassloader untuk setiap koneksi ke server, pada prinsipnya setiap server mungkin menggunakan versi berbeda dari kelas yang sama.

Saya telah menggunakan ide MultiClassloader dalam sebuah proyek, di mana kelas yang berisi skrip yang ditentukan pengguna harus dimuat dan dibongkar dari memori dan itu bekerja dengan cukup baik.

Mario Ortegón
sumber
31
Perhatikan juga bahwa menurut java.sun.com/docs/books/jls/second_edition/html/… pembongkaran kelas adalah sebuah optimasi dan, tergantung pada implementasi JVM, mungkin atau mungkin tidak benar-benar terjadi.
5
Sebagai alternatif yang lebih mudah dan ringan untuk OSGi, cobalah JBoss Modul - classloading termodulasi dengan classloader per modul (sekelompok toples).
Ondra Žižka
42

Ya ada cara untuk memuat kelas dan "membongkar" mereka nanti. Caranya adalah dengan mengimplementasikan classloader Anda sendiri yang berada di antara loader kelas tinggi (System class loader) dan pemuat kelas dari server aplikasi, dan berharap bahwa pemuat kelas server aplikasi tidak mendelegasikan pemuatan kelas ke pemuat atas. .

Kelas didefinisikan oleh paketnya, namanya, dan kelas loader yang awalnya dimuat. Program classloader "proxy" yang merupakan yang pertama kali dimuat saat memulai JVM. Alur kerja:

  • Program dimulai dan kelas "utama" yang sebenarnya dimuat oleh classloader proxy ini.
  • Setiap kelas yang kemudian dimuat secara normal (yaitu tidak melalui implementasi classloader lain yang dapat merusak hierarki) akan didelegasikan ke loader kelas ini.
  • Delegasi classloader proxy java.xdan sun.xke classloader sistem (ini tidak boleh dimuat melalui classloader lain selain classloader sistem).
  • Untuk setiap kelas yang dapat diganti, instantiate classloader (yang benar-benar memuat kelas dan tidak mendelegasikannya ke induk classloader) dan muat melalui ini.
  • Simpan paket / nama kelas sebagai kunci dan classloader sebagai nilai dalam struktur data (yaitu Hashmap).
  • Setiap kali proxy classloader mendapatkan permintaan untuk kelas yang dimuat sebelumnya, itu akan mengembalikan kelas dari loader kelas yang disimpan sebelumnya.
  • Seharusnya cukup untuk menemukan array byte kelas oleh loader kelas Anda (atau untuk "menghapus" pasangan kunci / nilai dari struktur data Anda) dan memuat kembali kelas jika Anda ingin mengubahnya.

Dilakukan di sana seharusnya tidak datang ClassCastException atau LinkageError dll.

Untuk informasi lebih lanjut tentang hierarki pemuat kelas (ya, itulah yang Anda terapkan di sini; -) lihat "Pemrograman Java Berbasis Server" oleh Ted Neward - buku itu membantu saya menerapkan sesuatu yang sangat mirip dengan yang Anda inginkan.

Georgi
sumber
3
Saya tidak mengerti orang-orang, yang mencentang -1 untuk jawaban ini tanpa meninggalkan komentar. Bagiku terlihat bagus. Mungkin memiliki satu ClassLoaderper kelas agak terlalu banyak, satu ClassLoaderper JAR masuk akal. Bisa lebih spesifik tentang bagaimana memaksa unggahan kelas dalam skema yang diusulkan? Misalnya bagaimana saya bisa menjamin, bahwa instance kelas yang dimuat oleh ClassLoaderA tidak dirujuk oleh instance yang dimuat oleh ClassLoaderB?
dma_k
@dma_k tepatnya, jawabannya bagus, tetapi tidak menyentuh poin-poin penting yang Anda sebutkan.
zinking
@ Geeorgi apakah ada implementasi yang sudah ada yang bisa kita mulai / gunakan kembali untuk ini?
Kereta luncur
1
Ini akan sangat sangat membantu, jika Anda bisa memberikan contoh kode java. Untuk lebih tepatnya, saya sedang mencari Cara membongkar kelas menggunakan CustomClassLoader tetapi tidak berhasil.
Sriharsha grv
@ Sriharshag.rv Sudahkah Anda mencoba ini dan menerapkan sampel?
niaomingjian
17

Saya menulis classloader kustom, dari mana dimungkinkan untuk membongkar kelas individu tanpa GCing classloader. Jar Class Loader

Kamran
sumber
Bekerja seperti pesona :). Apakah ada metode untuk membongkar semua file kelas dari file jar?
Ercksen
Sayangnya tidak pada saat ini. Tetapi akan melihat ke dalamnya. Mungkin ada di rilis mendatang.
Kamran
Ngomong-ngomong, saya menemukan sedikit solusi. Jika Anda memiliki satu JarClassLoaderuntuk setiap file jar yang Anda muat, Anda dapat memanggilnya getLoadedClasses(), lalu beralih ke masing-masing dan bongkar.
Ercksen
12

Classloader bisa menjadi masalah rumit. Anda terutama dapat mengalami masalah jika Anda menggunakan beberapa classloader dan tidak memiliki interaksinya yang jelas dan ketat. Saya pikir untuk benar-benar dapat membongkar sebuah kelas, Anda harus menghapus semua referensi ke setiap kelas (dan contoh mereka) yang Anda coba bongkar.

Kebanyakan orang perlu melakukan hal semacam ini pada akhirnya menggunakan OSGi . OSGi sangat kuat dan sangat ringan dan mudah digunakan,

Steve g
sumber
7

Anda dapat membongkar ClassLoader tetapi Anda tidak dapat membongkar kelas tertentu. Lebih khusus Anda tidak dapat membongkar kelas yang dibuat di ClassLoader yang tidak di bawah kendali Anda.

Jika memungkinkan, saya sarankan menggunakan ClassLoader Anda sendiri sehingga Anda dapat membongkar.

Jason Cohen
sumber
4

Kelas memiliki referensi kuat implisit untuk instance ClassLoader mereka, dan sebaliknya. Mereka adalah sampah yang dikumpulkan seperti benda Jawa. Tanpa menekan antarmuka alat atau yang serupa, Anda tidak dapat menghapus kelas individu.

Seperti biasa Anda bisa mendapatkan kebocoran memori. Referensi kuat ke salah satu kelas atau pemuat kelas Anda akan membocorkan semuanya. Ini terjadi dengan implementasi Sun dari ThreadLocal, java.sql.DriverManager dan java.beans, misalnya.

Tom Hawtin - tackline
sumber
-1

Jika Anda menonton langsung jika kelas pembongkaran bekerja di JConsole atau sesuatu, coba tambahkan juga java.lang.System.gc()di akhir logika pembongkaran kelas Anda. Ini secara eksplisit memicu Pengumpul Sampah.

Aleksander Drozd
sumber
3
Hati-hati: System.gc () tidak perlu memanggil GC. Ini hanya meminta jvm untuk memulainya, tetapi tidak menegakkannya. Dan IME sering tidak memulai GC: - \
Juh_