Programing

.NET에는 가비지 수집기가 있기 때문에 종료 자 / 소멸자 / 처리 패턴이 필요한 이유는 무엇입니까?

crosscheck 2020. 11. 11. 08:01
반응형

.NET에는 가비지 수집기가 있기 때문에 종료 자 / 소멸자 / 처리 패턴이 필요한 이유는 무엇입니까?


내가 올바르게 이해하면 .net 런타임은 항상 나를 정리합니다. 따라서 새 개체를 만들고 코드에서 참조를 중지하면 런타임이 해당 개체를 정리하고 그들이 차지한 메모리를 해제합니다.

이것이 왜 일부 객체에 소멸자 또는 폐기 메서드가 필요한 이유입니까? 더 이상 참조되지 않으면 런타임이 정리되지 않습니까?


파이널 라이저는 파일 핸들, 소켓, 커널 객체 등과 같은 부족한 리소스를 시스템으로 다시 릴리스하는 데 필요합니다. 파이널 라이저는 항상 객체 수명이 끝날 때 실행되기 때문에 해당 핸들을 릴리스 할 지정된 장소입니다.

Dispose패턴은 자원의 결정적 파괴를 제공하는 데 사용됩니다. .net 런타임 가비지 수집기는 비 결정적이므로 (런타임이 이전 개체를 수집하고 종료자를 호출 할시기를 확신 할 수 없음) 시스템 리소스의 결정적 해제를 보장하는 방법이 필요했습니다. 따라서 Dispose패턴을 올바르게 구현할 때 리소스의 결정적 해제를 제공하고 소비자가 부주의하고 개체를 처리하지 않는 경우 종료자가 개체를 정리합니다.

Dispose필요한 이유에 대한 간단한 예 는 빠르고 더티 로그 방법 일 수 있습니다.

public void Log(string line)
{
    var sw = new StreamWriter(File.Open(
        "LogFile.log", FileMode.OpenOrCreate, FileAccess.Write, FileShare.None));

    sw.WriteLine(line);

    // Since we don't close the stream the FileStream finalizer will do that for 
    // us but we don't know when that will be and until then the file is locked.
}

위의 예에서 파일은 가비지 수집기가 StreamWriter개체 에 대한 종료자를 호출 할 때까지 잠긴 상태로 유지됩니다 . 그 동안 로그를 작성하기 위해 메서드를 다시 호출 할 수 있기 때문에 문제가 발생하지만 이번에는 파일이 여전히 잠겨 있기 때문에 실패합니다.

올바른 방법은 개체를 사용한 후 처리하는 것입니다.

public void Log(string line)
{
    using (var sw = new StreamWriter(File.Open(
        "LogFile.log", FileMode.OpenOrCreate, FileAccess.Write, FileShare.None))) {

        sw.WriteLine(line);
    }

    // Since we use the using block (which conveniently calls Dispose() for us)
    // the file well be closed at this point.
}

BTW, 기술적으로 종료 자 및 소멸자는 동일한 의미입니다. C #과 달리 결정론적인 C ++ 소멸자와 사람들을 혼동하는 경향이 있기 때문에 C # 소멸자를 '종료 자'라고 부르는 것을 선호합니다.


이전 답변은 좋지만 여기서 중요한 점을 다시 한 번 강조하겠습니다. 특히 당신은

내가 올바르게 이해하면 .net 런타임은 항상 나를 정리합니다.

이것은 부분적으로 만 정확합니다. 실제로 .NET 은 하나의 특정 리소스 인 주 메모리에 대해서만 자동 관리를 제공합니다 . 다른 모든 리소스는 수동 정리가 필요합니다. 1)

이상하게도 메인 메모리는 프로그램 리소스에 대한 거의 모든 토론에서 특별한 지위를 얻습니다. 물론 여기에는 좋은 이유가 있습니다. 주 메모리는 종종 가장 부족한 리소스입니다. 그러나 관리가 필요한 다른 유형의 리소스도 있음을 기억할 가치가 있습니다.


1) 일반적으로 시도되는 해결책은 다른 리소스의 수명을 코드의 메모리 위치 또는 식별자의 수명에 연결하는 것입니다. 따라서 종료자가 존재합니다.


가비지 콜렉터는 실제로 일부 메모리를 확보해야하는 경우가 아니라면 시스템이 메모리 부족 상태가 아닌 경우에만 실행됩니다. 즉, GC가 언제 실행 될지 확신 할 수 없습니다.

이제 당신이 데이터베이스 연결이라고 상상해보십시오. 나중에 GC를 정리하면 필요한 것보다 훨씬 오래 데이터베이스에 연결되어 이상한로드 상황이 발생할 수 있습니다. 이 경우 IDisposable을 구현하여 사용자가 Dispose ()를 호출하거나 using ()을 사용하여 훨씬 나중에 실행될 수있는 GC에 의존하지 않고도 연결이 최대한 빨리 닫혔는지 확인할 수 있습니다.

일반적으로 IDisposable은 관리되지 않는 리소스로 작동하는 모든 클래스에서 구현됩니다.


  1. 가비지 컬렉터가 당신 이후에 정리할 수없는 것들이 있습니다.
  2. 정리할 있는 항목이 있어도 더 빨리 정리할 수 있습니다.

The real reason is because .net garbage collection is NOT designed to collect unmanaged resources, therefore the cleanup of these resources still lies in the hands of the developer. Also, object finalizers are not automatically called when the object goes out of scope. They are called by the GC at some undetermined time. And when they get called, GC doesn't run it right away, it waits for the next round to call it, increasing the time to clean up even more, not a good thing when your objects are holding scarce unmanaged resources (such as files or network connections). Enter the disposable pattern, where the developer can manually release scarce resources at a determined time (when calling yourobject.Dispose() or the using(...) statement). Keep in mind you should call GC.SuppressFinalize(this); in your dispose method to tell the GC that the object was manually disposed and shouldn't be finalized. I suggest you take a look at the Framework Design Guidelines book by K. Cwalina and B. Abrams. It explains the Disposable pattern very good.

Good Luck!


The simplistic explanation:

  • Dispose is designed for deterministic disposal of non-memory resources, especially scarce resources. For example, a window handle or a database connection.
  • Finalize is designed for non-deterministic disposal of non-memory resources, usually as a backstop if Dispose wasn't called.

Some guidelines for implementing the Finalize method:

  • Only implement Finalize on objects that require finalization, because there is a performance cost associated with Finalize methods.
  • If you require a Finalize method, consider implementing IDisposable to allow users of your type to avoid the cost of invoking the Finalize method.
  • Your Finalize methods should be protected rather than public.
  • Your Finalize method should free any external resources that the type owns, but only those that it owns. It should not reference any other resources.
  • The CLR doesn't make any guarantees as to the order in which Finalize methods are called. As Daniel notes in his comment, this means that a Finalize method should not access any member reference types if possible, because these may have (or may one day have) their own finalizers.
  • Never call a Finalize method directly on any type other than the type's base type.
  • Try to avoid any unhandled exception in your Finalize method, as that will terminate your process (in 2.0 or higher).
  • Avoid doing any long-running task in your Finalizer method, as that will block the Finalizer thread and prevent other Finalizer methods being executed.

Some guidelines for implementing the Dispose method:

  • Implement the dispose design pattern on a type that encapsulates resources that explicitly need to be freed.
  • Implement the dispose design pattern on a base type that has one or more derived types that hold on to resources, even if the base type does not.
  • After Dispose has been called on an instance, prevent the Finalize method from running by calling the GC.SuppressFinalize Method. The only exception to this rule is the rare situation in which work must be done in Finalize that is not covered by Dispose.
  • Do not assume that Dispose will be called. Unmanaged resources owned by a type should also be released in a Finalize method in the event that Dispose is not called.
  • Throw an ObjectDisposedException from instance methods on this type (other than Dispose) when resources are already disposed. This rule does not apply to the Dispose method because it should be callable multiple times without throwing an exception.
  • Propagate the calls to Dispose through the hierarchy of base types. The Dispose method should free all resources held by this object and any object owned by this object.
  • You should consider not allowing an object to be usable after its Dispose method has been called. Recreating an object that has already been disposed is a difficult pattern to implement.
  • Allow a Dispose method to be called more than once without throwing an exception. The method should do nothing after the first call.

Objects that need descructors and dispose methods is using un-managed resources. So the garbage collector cannot clean up those resources, and you have to do this on your own.

Look at the MSDN documentation for IDisposable; http://msdn.microsoft.com/en-us/library/system.idisposable.aspx

The example uses an un-managed handler - IntPr.


Some objects might need to clean up low-level items. Such as hardware that needs to be closed, etc.


Mainly for non-managed code, and interaction with non-managed code. "Pure" managed code should never need a finalizer. Disposable on the other hand is just a handy pattern to force something to be released when you are done with it.


There are a few (very few) cases where it may be necessary to perform a specific action when a pure managed object is no longer used, I can't come up with an example off the top of my head but I have seen a couple of legitimate uses over the years. But the main reason is to clean up any unmanaged resources that the object might be using.

So, in general, you won't need to use the Dispose/Finalize pattern unless you are using unmanaged resources.


Because the the Garbage Collector cannot collect what the managed environment did not allocate. Therefore any call to an unmanaged API that results in a memory allocation needs to be collected in the old fashioned way.


The .NET garbage collector knows how to handle managed objects within the .NET runtime. But the Dispose pattern (IDisposable) is used primarily for un-managed objects that an application is using.

In other words, the .NET runtime doesn't necessarily know how to deal with every type of device or handle out there (closing network connections, file handles, graphics devices, etc), so using IDisposable provides a way to say "let me implement some cleanup of my own" in a type. Seeing that implementation, the garbage collector can call Dispose() and ensure that things outside of the managed heap are cleaned up.

참고URL : https://stackoverflow.com/questions/331786/since-net-has-a-garbage-collector-why-do-we-need-finalizers-destructors-dispose

반응형