簡體   English   中英

為什么java和c#中有終結器?

[英]Why are there finalizers in java and c#?

我不太明白為什么有java和c#等語言的終結器。 AFAIK,他們:

  • 不保證運行(在java中)
  • 如果它們確實運行,它們可能會在有問題的對象成為最終確定的候選者之后運行任意時間量
  • 並且(至少在java中),即使堅持上課,它們也會產生驚人的巨大性能。

那他們為什么要加入呢? 我問了一個朋友,他嘟something了一些關於“你想盡可能地清理像數據庫連接這樣的東西”的東西,但這對我來說是一種不好的做法。 你為什么要依賴具有上述屬性的東西來做任何事情 ,即使是作為最后一道防線? 特別是當如果在任何API中設計類似的東西時,所述API將會被笑掉。

嗯,在某些情況下,它們非常有用。

在.NET CLR中,例如:

  • 不保證會運行

如果程序沒有被殺死,終結器將始終最終運行。 關於何時運行它並不確定。

  • 如果它們確實運行,它們可能會在有問題的對象成為最終確定的候選者之后運行任意時間量

確實如此,它們仍在運行。

在.NET中,這非常非常有用。 在.NET中將原生的非.NET資源包裝到.NET類中是很常見的。 通過實現終結器,您可以保證正確清理本機資源。 如果沒有這個,用戶將被迫調用一個方法來執行清理,這會大大降低垃圾收集器的效率。

通過實現終結器來確定何時釋放您的(本機)資源並不總是很容易,即使您的課程以不太完美的方式使用,您也可以保證它們能夠得到正確的清理。

  • 並且(至少在java中),即使堅持上課,它們也會產生驚人的巨大性能

同樣,.NET CLR的GC在這里具有優勢。 如果您實現了正確的接口( IDisposable ),並且如果開發人員正確實現了它,那么您可以防止發生最終的昂貴部分。 這樣做的方法是用戶定義的清理方法可以調用GC.SuppressFinalize ,它繞過終結器。

這為您提供了兩全其美的優勢 - 您可以實現終結器和IDisposable。 如果您的用戶正確處理您的對象,則終結器沒有任何影響。 如果他們不這樣做,終結器(最終)將運行並清理您的非托管資源,但在運行時會遇到(小)性能損失。

Hmya,你得到的照片畫得有點過於樂觀。 終結器也不能保證在.NET中運行。 典型的錯誤是終結器,它會在終結器線程上拋出異常或超時(2秒)。

當Microsoft決定在SQL Server中提供.NET托管支持時,這是一個問題。 重新啟動應用程序以解決資源泄漏的應用程序類型不被視為可行的解決方法。 .NET 2.0獲得了關鍵終結器,通過派生CriticalFinalizerObject類來實現。 這樣一個類的終結器必須遵守約束執行區(CERs)的規則,本質上是一個禁止異常的代碼區域。 您在CER中可以做的事情非常有限。

回到最初的問題,終結器是釋放內存以外的操作系統資源所必需的。 垃圾收集器管理內存非常好,但沒有做任何事情來釋放筆,畫筆,文件,套接字,窗口,管道等。當一個對象使用這樣的資源時,它必須確保在完成后釋放資源它。 即使程序忘記了,終結器也能確保發生這種情況。 您幾乎從不用自己的終結器編寫類,操作資源由框架中的類包裝。

.NET框架還有一個編程模式,以確保盡早釋放這樣的資源,以便在終結器運行之前資源不會延續。 所有具有終結器的類也實現了IDisposable.Dispose()方法,允許您的代碼顯式釋放資源。 這通常被.NET程序員所遺忘,但這通常不會導致問題,因為終結器確保最終完成。 許多.NET程序員已經失去了幾個小時的睡眠,擔心是否所有的Dispose()調用都得到了解決,並且大量的線程已經在論壇上啟動了。 Java人一定要快樂得多。


跟進您的評論:終結器線程中的異常和超時是您不必擔心的事情。 首先,如果你發現自己正在寫一個終結者,請深呼吸,問問自己是否在正確的道路上。 終結器適用於框架類,您應該使用這樣的類來使用操作資源,您將免費獲得內置於該類的終結器。 一直到SafeHandle類,他們都有一個關鍵的終結器。

其次,終結器線程故障是嚴重的程序故障。 類似於獲取OutOfMemory異常或絆倒電源線並拔出機器。 除了修復代碼中的錯誤或重新布線之外,您無法對它們做任何事情。 微軟設計關鍵終結器非常重要,他們不能依賴為SQL Server編寫.NET代碼的所有程序員來正確獲取代碼。 如果你自己摸索終結者,那么就沒有這樣的責任,你將接到客戶的電話,而不是微軟。

如果您讀取JavaDoc for finalize(),它表示“當垃圾收集確定沒有對該對象的更多引用時,由對象上的垃圾收集器調用。子類重寫finalize方法以處置系統資源或執行其他清理工作。“

http://java.sun.com/javase/6/docs/api/java/lang/Object.html#finalize

這就是“為什么”。 我想你可以爭論他們的實施是否有效。

我發現finalize()的最佳用途是通過釋放池化資源來檢測錯誤。 大多數泄漏的對象最終會被垃圾收集,您可以生成調試信息。

class MyResource {
    private Throwable allocatorStack;

    public MyResource() {
        allocatorStack = new RuntimeException("trace to allocator");
    }

    @Override
    protected void finalize() throws Throwable {
        try {
            System.out.println("Bug!");
            allocatorStack.printStackTrace();
        } finally {
            super.finalize();
        }
    }
 }

在java中,存在終結器以允許清理外部資源(存在於JVM之外的東西,並且當'父'java對象存在時不能被垃圾收集)。 這一直是罕見的。 例如,如果您正在與某些自定義硬件連接。

我認為java中的終結器不能保證運行的原因是它們可能沒有機會在程序終止時這樣做。

使用'純'java中的終結器可能會做的一件事就是用它來測試終止條件 - 例如檢查所有連接是否已關閉,如果不是則報告錯誤。 您無法保證始終會捕獲錯誤,但可能至少會在某些時間內捕獲到足以顯示錯誤的錯誤。

大多數java代碼都沒有調用終結器。

它們用於釋放本機資源(例如套接字,打開文件,設備),直到對象的所有引用都被破壞才能釋放,這是特定調用者(通常)無法解決的問題。會心。 替代方案是微妙的,不可能追蹤的資源泄漏......

當然,在許多情況下,作為應用程序作者, 您將知道只有一個對DB連接的引用(例如); 在這種情況下,當你知道你已經完成它時,終結器不能替代它。

在.Net land中,運行不保證t。 但他們會跑。

你在引用Object.Finalize嗎?

根據msdn,“在C#代碼中,無法調用或覆蓋Object.Finalize”。 實際上,他們建議使用Dispose方法,因為它更易於控制。

.NET中的終結器還有一個復雜的問題。 如果類具有終結器並且沒有得到Dispose()'d,或者Dispose()沒有抑制終結器,則垃圾收集器將推遲收集,直到壓縮第2代內存(上一代),因此對象是“排序” “但不是內存泄漏。 (是的,它最終會被清理干凈,但很可能直到應用程序終止。)

正如其他人所提到的,如果一個對象擁有非托管資源,它應該實現IDisposable模式。 開發人員應該知道,如果一個對象實現了IDisposable,那么應該始終調用它的Dispose()方法。 C#提供了一種使用using語句自動執行此操作的方法:

using (myDataContext myDC = new myDataContext())
{
    // code using the data context here
}

using塊在塊退出時自動調用Dispose(),甚至通過拋出返回或異常退出。 using語句僅適用於實現IDisposable的對象。

並且要注意另一個混亂點; Dispose()方法是對象釋放資源的機會,但它實際上並沒有釋放的Dispose()'d對象。 當沒有對它們的活動引用時,.NET對象可以進行垃圾收集。 從技術上講,從AppDomain開始,任何對象引用鏈都無法訪問它們。

C ++中destructor()的等價物是Java中的finalizer()

當對象的生命周期即將結束時調用它們。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM