我們已經討論過了gc的工作方式以及gc root。我提到過實時堆疊會被視為gc root。實時堆疊包括正在執行的執行緒中的所有區域性變數和呼叫堆疊的成員。
如果出於某種原因,你要建立乙個永遠執行的不執行任何操作並且具有對物件引用的執行緒,那麼這將會導致記憶體洩漏。
這種情況很容易發生的乙個例子是使用timer。考慮以下**:
public class myclass
private void handletick(object state)
}
如果你並沒有真正的停止這個timer,那麼它會在乙個單獨的執行緒中執行,並且由於引用了乙個myclass的例項,因此會阻止該例項被收集。
到目前為止,我們僅僅談論了託管記憶體,也就是由垃圾收集器管理的記憶體。非託管記憶體是完全不同的問題,你將需要顯式地**記憶體,而不僅僅是避免不必要的引用。
這裡有乙個簡單的例子。
public class someclass
// do stuff without freeing the buffer memory
}
在上述方法中,我們使用了marshal.allochglobal方法,它分配了非託管記憶體緩衝區。
在這背後,allochglobal會呼叫kernel32.dll中的localalloc函式。如果沒有使用marshal.freehglobal顯式地釋放控制代碼,則該緩衝區記憶體將被視為占用了程序的記憶體堆,從而導致記憶體洩漏。
要解決此類問題,你可以新增乙個dispose方法,以釋放所有非託管資源,如下所示:
public class someclass : idisposable
public void dispose()
}
由於記憶體碎片問題,非託管記憶體洩漏比託管記憶體洩漏更嚴重。垃圾**器可以移動託管記憶體,從而為其他物件騰出空間。但是,非託管記憶體將永遠卡在它的位置。
在最後乙個示例中,我們新增了dispose方法以釋放所有非託管資源。這很棒,但是當有人使用了該類卻沒有呼叫dispose時會發生什麼呢?
為了避免這種情況,你可以在c#中使用using語句:
using (var instance = new myclass())
這適用於實現了idisposable介面的類,並且編譯器會將其轉化為下面的形式:
myclass instance = new myclass();;
tryfinally
這非常有用,因為即使丟擲異常,也會呼叫dispose。
你可以做的另一件事是利用dispose pattern。下面的示例演示了這種情況:
public class myclass : idisposable
protected virtual void dispose(bool disposing)
// free any unmanaged objects here.
marshal.freehglobal(_bufferptr);
_disposed = true;
}public void dispose()
~myclass()
}
這種模式可確保即使沒有呼叫dispose,dispose也將在例項被垃圾**時被呼叫。另一方面,如果呼叫了dispose,則finalizer將被抑制(suppressfinalize)。抑制finalizer很重要,因為finalizer開銷很大並且會導致效能問題。
然而,dispose-pattern不是萬無一失的。如果從未呼叫dispose並且由於託管記憶體洩漏而導致你的類沒有被垃圾**,那麼非託管資源也將不會被釋放。
記憶體洩漏和記憶體溢位 記憶體洩漏和記憶體溢位
記憶體洩漏 是指申請的記憶體空間使用完畢之後未 一次記憶體洩露危害可以忽略,但若一直洩漏,無論有多少記憶體,遲早都會被占用光,最終導致程式crash。因此,開發中我們要盡量避免記憶體洩漏的出現 記憶體溢位 是指程式在申請記憶體時,沒有足夠的記憶體空間供其使用。通俗理解就是記憶體不夠用了,通常在執行大...
wxWidgets delete記憶體洩漏
定義乙個結構typedef tag struct customnode struct,再定義乙個wxarrayptrvoid m nodearray 在程式的初始化過程裡 for wxint32 i 0 i 5 i node struct ptemp new node struct ptemp a ...
PHP記憶體洩漏
如果php物件存在遞迴引用,就會出現記憶體洩漏。這個bug在php裡已經存在很久很久了,先讓我們來重現這個bug,如下 class foo class bar for i 0 i 100 i 執行以上 你會發現,記憶體使用量本應該不變才對,可實際上卻是不斷增加,unset沒有完全生效。現在的開發很多...