가비지 콜렉터가 IDisposable을 호출합니까?
닷넷 으로 IDisposable 패턴은 의미 당신이 종료자가 작성하고는 IDisposable을 구현하는 경우, 귀하의 종료 자 요구가 명시 적으로 폐기를 호출 할 수 있다는 것이다. 이것은 논리적 인 것이며, 파이널 라이저가 보증되는 드문 상황에서 항상 수행 한 것입니다.
그러나 내가 이렇게하면 어떻게됩니까?
class Foo : IDisposable
{
public void Dispose(){ CloseSomeHandle(); }
}
종료 자 또는 다른 것을 구현하지 마십시오. 프레임 워크에서 Dispose 메서드를 호출합니까?
예, 나는 이것이 멍청한 소리라는 것을 알고 모든 논리가 그렇지 않다는 것을 암시하지만 항상 머리 뒤쪽에 2 가지가있어서 확실하지 않았습니다.
몇 년 전에 누군가가 실제로 그렇게 할 것이라고 말했고 그 사람은 "자신의 지식을 알고있다"는 매우 탄탄한 기록을 가지고있었습니다.
컴파일러 / 프레임 워크는 구현하는 인터페이스 (예 : foreach, 확장 메소드, 속성을 기반으로하는 직렬화 등)에 따라 다른 '마법'을 수행하므로 이것이 '마법'일 수도 있습니다.
나는 그것에 대해 많은 것을 읽었으며 많은 암시가 있었지만 이 질문에 대한 확실한 예 또는 아니오 대답을 찾을 수 없었습니다 .
.Net 가비지 콜렉터는 가비지 콜렉션에서 오브젝트의 Object.Finalize 메소드를 호출합니다. 하여 기본 이하지 않습니다 아무것도 하고 추가 자원을 확보하려는 경우 오버라이드 (override)해야합니다.
Dispose는 자동으로 호출되지 않으며 'using'또는 'try finally'블록과 같이 리소스를 해제 해야하는 경우 명시 적으로 호출 해야합니다.
자세한 내용 은 http://msdn.microsoft.com/en-us/library/system.object.finalize.aspx 를 참조하십시오.
나는 그의 의견에서 Brian의 요점을 강조하고 싶습니다. 그것이 중요하기 때문입니다.
파이널 라이저는 C ++ 에서처럼 결정 론적 소멸자가 아닙니다. 다른 사람들이 지적했듯이,이 호출 될 때의 보장이없고, 당신이 충분한 메모리를 가지고 있다면 참으로 그것은한다면 지금까지 호출 할 수.
그러나 파이널 라이저의 나쁜 점은 Brian이 말했듯이 객체가 가비지 콜렉션에서 살아남는다는 것입니다. 이것은 나쁠 수 있습니다. 왜?
아시다시피, GC는 0 세대, 1 세대 및 2 세대와 대형 객체 힙으로 나뉩니다. 스플릿 (Split)은 느슨한 용어입니다. 하나의 메모리 블록을 얻지 만 Gen 0 객체가 시작하고 끝나는 위치에 대한 포인터가 있습니다.
사고 과정은 수명이 짧은 많은 물건을 사용할 것입니다. 따라서 GC가 Gen 0 객체에 쉽고 빠르게 도달 할 수 있어야합니다. 따라서 메모리 부족이있을 때 가장 먼저하는 일은 Gen 0 컬렉션입니다.
그래도 충분한 압력이 해결되지 않으면 다시 돌아가서 Gen 1 스윕 (Gen 0 다시 실행)을 수행 한 다음 여전히 충분하지 않은 경우 Gen 2 스윕 (Gen 1 및 Gen 0 다시 실행)을 수행합니다. 따라서 수명이 긴 개체를 정리하면 시간이 걸리고 비용이 많이들 수 있습니다 (작업 중에 스레드가 중단 될 수 있으므로).
이것은 당신이 이런 식으로하면 :
~MyClass() { }
개체가 무엇이든 관계없이 Generation 2에 적용됩니다. 이는 GC가 가비지 수집 중에 종료자를 호출 할 방법이 없기 때문입니다. 따라서 마무리 해야하는 객체는 다른 스레드 (파이널 라이저 스레드-죽일 경우 모든 종류의 나쁜 일이 발생 함)로 정리하기 위해 특수 대기열로 이동됩니다. 즉, 객체가 오래 걸려서 가비지 수집이 더 많이 발생할 수 있습니다.
따라서 IDisposable을 사용하여 가능할 때마다 리소스를 정리하고 종료자를 사용하는 방법을 심각하게 찾으려고합니다. 응용 프로그램의 가장 큰 관심사입니다.
이미 여기에 좋은 토론이 많이 있으며 파티에 약간 늦었지만 직접 몇 가지 요점을 추가하고 싶었습니다.
- 가비지 콜렉터는 절대 Dispose 메소드를 직접 실행하지 않습니다.
- GC 는 느낌이들 때 종료 자를 실행합니다.
- 종료자가있는 객체에 사용되는 일반적인 패턴 중 하나는 Dispose (bool disposing)로 정의 된 메서드를 호출하여 명시 적 Dispose 호출이 아니라 종료로 인해 호출되었음을 나타냅니다.
- 개체를 마무리하는 동안 다른 관리되는 개체에 대해 어떤 가정을하는 것은 안전하지 않기 때문입니다 (이미 마무리되었을 수 있음).
class SomeObject : IDisposable {
IntPtr _SomeNativeHandle;
FileStream _SomeFileStream;
// Something useful here
~ SomeObject() {
Dispose(false);
}
public void Dispose() {
Dispose(true);
}
protected virtual void Dispose(bool disposing) {
if(disposing) {
GC.SuppressFinalize(this);
//Because the object was explicitly disposed, there will be no need to
//run the finalizer. Suppressing it reduces pressure on the GC
//The managed reference to an IDisposable is disposed only if the
_SomeFileStream.Dispose();
}
//Regardless, clean up the native handle ourselves. Because it is simple a member
// of the current instance, the GC can't have done anything to it,
// and this is the onlyplace to safely clean up
if(IntPtr.Zero != _SomeNativeHandle) {
NativeMethods.CloseHandle(_SomeNativeHandle);
_SomeNativeHandle = IntPtr.Zero;
}
}
}
그것은 간단한 버전이지만,이 패턴을 따라 잡을 수있는 많은 뉘앙스가 있습니다.
- IDisposable.Dispose 계약은 여러 번 호출하는 것이 안전해야 함을 나타냅니다 (이미 삭제 된 개체에서 Dispose를 호출하면 아무 작업도 수행하지 않아야 함)
- It can get very complicated to properly manage an inheritance hierarchy of disposable objects, especially if different layers introduce new Disposable and unmanaged resources. In the pattern above Dispose(bool) is virtual to allow it to be overridden so that it can be managed, but I find it to be error-prone.
In my opinion, it is much better to completely avoid having any types that directly contain both disposable references and native resources that may require finalization. SafeHandles provide a very clean way of doing this by encapsulating native resources into disposable that internally provide their own finalization (along with a number of other benefits like removing the window during P/Invoke where a native handle could be lost due to an asynchronous exception).
Simply defining a SafeHandle makes this Trivial:
private class SomeSafeHandle
: SafeHandleZeroOrMinusOneIsInvalid {
public SomeSafeHandle()
: base(true)
{ }
protected override bool ReleaseHandle()
{ return NativeMethods.CloseHandle(handle); }
}
Allows you to simplify the containing type to:
class SomeObject : IDisposable {
SomeSafeHandle _SomeSafeHandle;
FileStream _SomeFileStream;
// Something useful here
public virtual void Dispose() {
_SomeSafeHandle.Dispose();
_SomeFileStream.Dispose();
}
}
I don't think so. You have control over when Dispose is called, which means you could in theory write disposal code that makes assumptions about (for instance) the existence of other objects. You have no control over when the finalizer is called, so it would be iffy to have the finalizer automatically call Dispose on your behalf.
EDIT: I went away and tested, just to make sure:
class Program
{
static void Main(string[] args)
{
Fred f = new Fred();
f = null;
GC.Collect();
GC.WaitForPendingFinalizers();
Console.WriteLine("Fred's gone, and he's not coming back...");
Console.ReadLine();
}
}
class Fred : IDisposable
{
~Fred()
{
Console.WriteLine("Being finalized");
}
void IDisposable.Dispose()
{
Console.WriteLine("Being Disposed");
}
}
Not in the case you describe, But the GC will call the Finalizer for you, if you have one.
HOWEVER. The next garbage collection ,instead of being collected, the object will go into the finalization que, everything gets collected, then it's finalizer called. The next collection after that it will be freed.
Depending on the memory pressure of your app, you may not have a gc for that object generation for a while. So in the case of say, a file stream or a db connection, you may have to wait a while for the unmanaged resource to be freed in the finalizer call for a while, causing some issues.
No, it's not called.
But this makes easy to don't forget to dispose your objects. Just use the using
keyword.
I did the following test for this:
class Program
{
static void Main(string[] args)
{
Foo foo = new Foo();
foo = null;
Console.WriteLine("foo is null");
GC.Collect();
Console.WriteLine("GC Called");
Console.ReadLine();
}
}
class Foo : IDisposable
{
public void Dispose()
{
Console.WriteLine("Disposed!");
}
The GC will not call dispose. It may call your finalizer, but even this isn't guaranteed under all circumstances.
See this article for a discussion of the best way to handle this.
The documentation on IDisposable gives a pretty clear and detailed explaination of the behavior, as well as example code. The GC will NOT call the Dispose()
method on the interface, but it will call the finalizer for your object.
The IDisposable pattern was created primarily to be called by the developer, if you have an object that implements IDispose the developer should either implement the using
keyword around the context of the object or call the Dispose method directly.
The fail safe for the pattern is to implement the finalizer calling the Dispose() method. If you don't do that you may create some memory leaks i.e.: If you create some COM wrapper and never call the System.Runtime.Interop.Marshall.ReleaseComObject(comObject) (which would be placed in the Dispose method).
There is no magic in the clr to call Dispose methods automatically other than tracking objects that contain finalizers and storing them in the Finalizer table by the GC and calling them when some clean up heuristics kick in by the GC.
참고URL : https://stackoverflow.com/questions/45036/will-the-garbage-collector-call-idisposable-dispose-for-me
'Programming' 카테고리의 다른 글
BCrypt는 C #에서 사용하기에 적합한 해싱 알고리즘입니까? (0) | 2020.07.07 |
---|---|
숫자가 정규 표현식의 소수인지 확인하는 방법은 무엇입니까? (0) | 2020.07.07 |
문자열의 0은 무엇입니까? (0) | 2020.07.07 |
NetBeans에서 캐시를 지우는 방법 (0) | 2020.07.06 |
파이썬에서 RPC를 수행하기위한 현재의 선택은 무엇입니까? (0) | 2020.07.06 |