2011-01-26 99 views
3

聲明:我知道如何在JVM中加載類以及如何以及何時卸載它們。問題不在於當前的行爲,問題是,爲什麼JVM不支持「強制」類/類加載器卸載?它可能具有以下語義:當classloader被「強制卸載」時,它所加載的所有類都被標記爲「unloaded」,意味着不會創建新實例(將引發異常,如「ClassUnloadedException」)。然後,這樣的卸載類的所有實例也被標記爲「卸載」,因此每次訪問它們都會拋出InstanceUnloadedException(就像NullPointerException)。爲什麼JVM不支持強制類/類加載器卸載?

執行:我認爲,這可以在垃圾收集期間完成。例如,壓縮收集器無論如何都會移動活動對象,因此它可以檢查當前對象的類是否被「卸載」,而不是移動對象將引用更改爲受保護頁面的內存(訪問它將引發上述InstanceUnloadedException)。這也會有效地造成對象垃圾。或者可能在GC的「標記」階段完成。無論如何,我認爲這在技術上是可行的,在沒有「卸載」發生時幾乎沒有開銷。

的理由:它是可容忍的,而整個的JVM故障是不希望的這種「硬核」機制可以用於其中很多動態代碼重新加載的發生運行時和特定應用或部分的故障是有用的。例如,應用程序服務器和OSGi運行時。

根據我的經驗,在9個10的情況下動態重新部署會導致PermGenSpace,因爲引用沒有被正確清理(例如填充長效線程的靜態字段中的ThreadLocal)。此外,有一個明確的例外而不是難以調試的泄漏可能有助於拋光代碼,所以沒有任何引用泄露到長期生存的範圍內而不受控制。

您認爲如何?

+0

我還想了一會兒,但是讓程序試圖處理一個可能突然「失蹤」的類的代價會產生很多額外的邏輯和新的複雜性。是使問題變得更糟的解決方案?不確定... – 2011-01-26 04:24:58

+0

如果你想要不同的行爲,你總是可以選擇編寫你自己的類加載器。 – 2011-01-26 04:27:09

回答

3

此功能只會造成混亂和混亂。強制卸載一個類會帶來很多問題,比如不推薦使用Thread.stop(),除此之外會更糟糕。

只是爲了進行比較,Thread.stop()往往因爲突然的線程中斷而導致許多處於不一致狀態的對象,並且線程可能正在執行任何類型的代碼。在實踐中對此進行編碼是不可能的,或者至少是極大的努力。在這種情況下,認爲在幾乎不可能和完全不可能寫出正確的多線程代碼。

在你的情況下,這種功能會有類似的不良副作用,但規模要差得多。代碼可能意外地在任何地方得到異常,所以在實踐中很難或不可能對它進行防禦編碼或處理它。假設你有一個嘗試塊來做一些IO,然後一個類被無人地卸載。該代碼將在一個意外的地方拋出一個ClassUnloadedException,可能會使對象處於不一致的狀態。如果您嘗試防禦代碼,則由於另一個意外的ClassUnloadedException,負責該防禦的代碼也可能會失敗。如果您有一個試圖關閉資源的finally塊,並且該塊內引發了一個ClassUnloadedException,則該資源無法關閉。再次,處理它會非常困難,因爲處理程序也可能會得到一個ClassUnloadedException。

順便說一句,NullPointerException是完全可預測的。你得到一個指向null的指針並試圖去解釋它。通常這是一個編程錯誤,但行爲是完全可預測的。ClassCastException,IllegalStateException,IllegalArgumentException和其他RuntimeException在可預測的條件下(或至少應該)發生。不是RuntimeException的異常有時可能會意外發生(如IOException),但編譯器會強制您處理或重新拋出它們。

另一方面,StackOverflowError,OutOfMemoryError,ExceptionIninitializerError和NoClassDefFoundError是不可預知的事情,可能發生在代碼中的任何地方,很少有可能做些事情來處理或恢復它們。當一些程序擊中時,通常他們會瘋狂地發瘋。少數人試圖處理它們,限制警告用戶它必須立即終止,也許試圖保存未保存的數據。你的ClassUnloadedException是一個典型的事情,它可能是一個ClassUnloadedError。它會像ExceptionIninitializerError或NoClassDefFoundError一樣表現出來,在99%的情況下,這意味着你的應用程序被完全破壞了,除非它會更糟糕,因爲它沒有一個快速失敗的行爲,所以它變得更加隨機,對它不可預知。

動態重新部署就其性質而言,是JVM/Container中可能發生的最醜陋的黑客攻擊之一,因爲它徹底改變了已經運行的某些代碼,這往往會讓你變得非常不穩定隨機馬車行爲。但它有其價值,因爲它在調試中有很大幫助。因此,防止容器實現的不規則行爲是爲正在運行的程序創建一組新的類並與較舊的程序共享內存。如果程序的新部分和舊部分不直接通信(即僅通過兼容序列化或根本沒有通信),那麼您沒有問題。如果沒有發生結構變化,並且沒有生命對象取決於某些更改的特定實現細節,則通常也是安全的。如果你遵循這些規則,則不會顯示ClassUnloadedError。然而,在某些情況下,您可能不遵守這些規則並且仍然是安全的,例如修復方法中的錯誤並更改其參數,並且不存在依賴於錯誤的活動對象(即,它們從未存在或它們都已死亡) 。

但是,如果您確實希望在訪問較舊部分的對象時拋出ClassUnloadedError,因爲此行爲會標記其中一個隔離規則已損壞,然後將所有內容都放下。因此,在同一時間內部署新舊程序是沒有意義的,只是將其完全重新部署會更簡單。

而關於GC中的實現,它不起作用。一個死對象已經死了,不管它的類對象是死還是死。卸載類的活動對象不能被垃圾收集,因爲它們仍然可以到達其他對象,無論每個方法的實現是否會奇蹟般地改變爲總是拋出異常/錯誤的東西。在任何實現中,回溯對象的引用都是非常昂貴的操作,多線程會更糟,可能會導致完美的活動對象和類中的嚴重性能下降。

此外,動態加載類不適用於生產用途,僅適用於開發人員測試。因此,爲這個功能購買所有麻煩和複雜性是沒有意義的。結論是,在實踐中,您的想法創建了一些類似於Thread.stop()的類似於NoClassdefFoundError的東西,但強於兩者的總和。像女王一樣,象棋中的主教和白嘴鴉組合,但比兩者的總和更強。這是一個非常糟糕的主意。