2009-04-27 4 views
6

실제로는 GC.SuppressFinalize이 파이널 라이저에 대한 호출을 항상 억제하지는 않는다는 사실을 관찰했습니다. 그렇다고해서 파이널 라이저가 호출 될 수도 있습니다. 따라서 요청이 시스템이 아닌이 아닌 GC.SuppressFinalize인지 여부가 궁금합니다.GC.SuppressFinalize는 보장됩니까?


추가 정보

다음 정보가 필요한 경우 후 질문에 대한 더 많은 컨텍스트를 제공하는 데 도움이 될 수 있습니다. 시스템이 지정된 개체에 대한 종료자를 호출하지

요청 :

GC.SuppressFinalize 문서 요약 요청입니다 상태를 않습니다.

나는 이것이 일상적으로 사용되는 단어인지, 아니면 실제로 런타임 동작을 설명하기위한 단어인지 궁금합니다.

Schnell 프로젝트에서 취한 을 기반으로 한 다음의 SingletonScope 클래스에서 이것을 관찰 해 보았습니다. 아이디어는 디버그 빌드에서 Dispose 메서드가 호출되었는지 여부를 감지하는 것입니다. 그렇지 않다면 최종 결정자가 결국에는 킥을하고 경고를 제기 할 수 있습니다. Dispose이 호출되면 GC.SuppressFinalize이어야합니다.은 종료자가 파이어 발생을 방지해야합니다. 불행히도, 경고는 어떻게 든 발생하는 것처럼 보이지만 결정 론적 인 방식은 아닙니다. 즉, 그들은 매 달마다 발사하지 않습니다.

#region License, Terms and Author(s) 
// 
// Schnell - Wiki widgets 
// Copyright (c) 2007 Atif Aziz. All rights reserved. 
// 
// Author(s): 
//  Atif Aziz, http://www.raboof.com 
// 
// This library is free software; you can redistribute it and/or modify it 
// under the terms of the GNU Lesser General Public License as published by 
// the Free Software Foundation; either version 2.1 of the License, or (at 
// your option) any later version. 
// 
// This library is distributed in the hope that it will be useful, but WITHOUT 
// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 
// FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public 
// License for more details. 
// 
// You should have received a copy of the GNU Lesser General Public License 
// along with this library; if not, write to the Free Software Foundation, 
// Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 
// 
#endregion 

namespace WikiPad 
{ 
    #region Imports 

    using System; 
    using System.Diagnostics; 

    #endregion 

    // 
    // NOTE: To use SingletonScope and ISingletonScopeHelper with value 
    // types, use Nullable<T>. For example, if the type of value to scope 
    // is ThreadPriority then use ISingletonScopeHelper<ThreadPriority?> 
    // and SingletonScope<ThreadPriority?>. 
    // 

    // 
    // In debug builds, this type is defined as a class so a finalizer 
    // can be used to detect an undisposed scope. 
    // 

    /// <summary> 
    /// Designed to change a singleton and scope that change. After exiting 
    /// the scope, the singleton is restored to its value prior to entering 
    /// the scope. 
    /// </summary> 

    #if !DEBUG 
    internal struct SingletonScope<T, H> 
    #else 
    internal sealed class SingletonScope<T, H> 
    #endif 
     : IDisposable 
     where H : ISingletonScopeHelper<T>, new() 
    { 
     private T _old; 

     public SingletonScope(T temp) 
     { 
      _old = Helper.Install(temp); 
     } 

     private static H Helper 
     { 
      get { return new H(); } 
     } 

     public void Dispose() 
     { 
      // 
      // First, transfer fields to stack then nuke the fields. 
      // 

      var old = _old; 
      _old = default(T); 

      // 
      // Shazam! Restore the old value. 
      // 

      Helper.Restore(old); 

      #if DEBUG 
      GC.SuppressFinalize(this); // Only when defined as a class! 
      #endif 
     } 

     #if DEBUG 

     // 
     // This finalizer is used to detect an undisposed scope. This will 
     // only indicate that the scope was not disposed but (unfortunately) 
     // not which one and where since GC will probably collect much later 
     // than it should have been disposed. 
     // 

     ~SingletonScope() 
     { 
      Debug.Fail("Scope for " + typeof(T).FullName + " not disposed!"); 
     } 

     #endif 
    } 
} 

전체 작업 예제는 편집 지침에 http://gist.github.com/102424에서 사용할 수 있지만 문제는 지금까지 결정 론적으로 복제 할 수 없습니다 수 있습니다 않습니다.

+0

Trace in Dispose 메서드가 없으므로 finalizer 전에 성공적으로 호출되었다고 확신합니까? – Groo

+0

@Groo : 예 C#을 사용하지 않는 한 확실합니다. :) –

답변

0

동일한 패턴을 여러 번 사용하고 GC.SupressFinalize가 항상 작동하는 것으로 나타났습니다.

GC.ReRegisterForFinalize를 호출하면 개체가 최종화를 위해 다시 등록됩니다.

위의 기술을 사용할 때마다 항상 객체 구성 중에 전체 스택 추적을 포함하므로 배치되지 않은 객체를 할당 한 메소드를 추적 할 수 있습니다.

예 : 생성자에서 사용

StackFrame frame = new StackFrame(1); 

그리고 finaliser 동안 디버그 메시지에보고하십시오.

또한 GC.SupressFinalize가 finally 절에 없다는 것을 알았습니다. 처리 중에 예외가 발생하면 개체 파이널 라이저가 표시되지 않습니다.

+0

보시다시피, 함께 제공되는 코드에서 GC.ReRegisterForFinalize를 호출 할 필요가 없습니다. –

+0

나는 그 대답이 완전하다는 것을 보장하려고 노력했다는 것을 알고있다. stackframe 디버깅 트릭을 시도 했는가? –

+0

@ sambo99 트릭을 추가하는 것을 고려해 보겠습니다. 그 사이에 scope 객체가 C#에서 사용하는 것과 함께 사용되기 때문에 Dispose가 호출되었는지 확실히 알기 때문에이 경우 도움이되지 않습니다. 나는 파이널 라이저에서 좀 더 방어가 필요한지 궁금해하고 있었다. –

4

이상한 사람 은 인스턴스 메서드가 아직 실행 중이 아니더라도 해당 인스턴스 메서드가 나중에 변수를 사용하지 않는 한 finalizer가 계속 실행될 수 있습니다.따라서 예제 코드에서 Dispose 메서드는 첫 번째 줄 다음에 인스턴스 변수를 사용하지 않습니다. Dispose이 아직 실행 중이더라도 인스턴스를 완료 할 수 있습니다.

Dispose 메서드 끝에 GC.KeepAlive(this)에 대한 호출을 삽입하면 이 문제가없는 것으로 나타납니다.

크리스 Brumme 이것에 대해 blog post을 가지고 있으며, 나는 있다고 생각 다른 주변 어딘가에 ... 난 항상는 IDisposable 인터페이스를 구현하기 위해이 디자인 패턴을 사용하고

+0

Jon, 나는 그 경고에 대해 잘 알고 있지만 Finalizer와 Debug.Fail 코드는 응용 프로그램이 존재할 때 발사되는 것처럼 보입니다.이 응용 프로그램은 대기중인 finalizer가 실행되고 있다고 가정합니다. GC.SuppressFinalize가 호출 된 후 * long *이 발생합니다. –

+0

@Jon Dispose를 깨뜨린 일부 샘플 코드는 깨달음이 될 것입니다. 샘플이 뒤 틀릴 수 있는지 궁금합니다. –

+0

@ sambo99 : 지금 샘플을 작성할 시간이 없지만 나중에 시도하겠습니다. 기본적으로 Dispose를 GC.Collect()/GC.WaitForPendingFinalizers를 호출하면 Dispose가 끝나기 전에 인스턴스를 완료하는 것을 볼 수 있습니다. –

3

. (마이크로 소프트가 제안한) GC.SuppressFinalize는 항상 보증의 본질을 가지고 있습니다!

using System; 
using System.ComponentModel; 

//The following example demonstrates how to use the GC.SuppressFinalize method in a resource class to prevent the clean-up code for the object from being called twice. 

public class DisposeExample 
{ 
    // A class that implements IDisposable. 
    // By implementing IDisposable, you are announcing that 
    // instances of this type allocate scarce resources. 
    public class MyResource : IDisposable 
    { 
     // Pointer to an external unmanaged resource. 
     private IntPtr handle; 
     // Other managed resource this class uses. 
     private readonly Component component = new Component(); 
     // Track whether Dispose has been called. 
     private bool disposed; 

     // The class constructor. 
     public MyResource(IntPtr handle) 
     { 
      this.handle = handle; 
     } 

     // Implement IDisposable. 
     // Do not make this method virtual. 
     // A derived class should not be able to override this method. 
     public void Dispose() 
     { 
      Dispose(true); 
      // This object will be cleaned up by the Dispose method. 
      // Therefore, you should call GC.SupressFinalize to 
      // take this object off the finalization queue 
      // and prevent finalization code for this object 
      // from executing a second time. 
      GC.SuppressFinalize(this); 
     } 

     // Dispose(bool disposing) executes in two distinct scenarios. 
     // If disposing equals true, the method has been called directly 
     // or indirectly by a user's code. Managed and unmanaged resources 
     // can be disposed. 
     // If disposing equals false, the method has been called by the 
     // runtime from inside the finalizer and you should not reference 
     // other objects. Only unmanaged resources can be disposed. 
     private void Dispose(bool disposing) 
     { 
      // Check to see if Dispose has already been called. 
      if (!disposed) 
      { 
       // If disposing equals true, dispose all managed 
       // and unmanaged resources. 
       if (disposing) 
       { 
        // Dispose managed resources. 
        component.Dispose(); 
       } 

       // Call the appropriate methods to clean up 
       // unmanaged resources here. 
       // If disposing is false, 
       // only the following code is executed. 
       CloseHandle(handle); 
       handle = IntPtr.Zero; 
      } 
      disposed = true; 
     } 

     // Use interop to call the method necessary 
     // to clean up the unmanaged resource. 
     [System.Runtime.InteropServices.DllImport("Kernel32")] 
     private extern static Boolean CloseHandle(IntPtr handle); 

     // Use C# destructor syntax for finalization code. 
     // This destructor will run only if the Dispose method 
     // does not get called. 
     // It gives your base class the opportunity to finalize. 
     // Do not provide destructors in types derived from this class. 
     ~MyResource() 
     { 
      // Do not re-create Dispose clean-up code here. 
      // Calling Dispose(false) is optimal in terms of 
      // readability and maintainability. 
      Dispose(false); 
     } 
    } 

    public static void Main() 
    { 
     // Insert code here to create 
     // and use a MyResource object. 
    } 
} 

자료 : 사용자 정의 파이널 가진 개체가 구축 MSDN: GC.SuppressFinalize Method

+0

빙고! 귀하의 구현은 finalizer 억제 여부에 상관없이 finalizer가 호출되는지 여부에 상관없이 방어 적으로 코딩됩니다. Finalizer는 Dispose를 다시 호출하고 폐기되었는지 여부를 확인하기 위해 추가 플래그를 갖습니다. 내가 Finalizer에서 Debug.Fail에 벌거 벗은 호출을 배치 했으므로 GC.SuppressFinalize를 사용하면 호출되지 않을 것이라고 가정하고 런타임 보증에 대한 나의 질문을 던집니다. –

+2

코드를 실행하면 Dispose 메서드가 호출되지 않는 경우에만 소멸자 (finalizer)가 실행된다는 것을 알 수 있습니다. – CSharper

+0

부활 추적 WeakReference가 존재하는 동안 객체가 대기열에 들어오고 WeakReference가 강한 참조로 다시 변환되는 경우 동일한 핸들에서 CloseHandle을 두 번 호출 할 수없는 이유가 있습니까? 바른 시간? I2nterlocked.Exchange 또는 Interlocked.CompareExchange를 사용하면 처분 플래그를 처리 할 때이를 피할 수 있습니다. – supercat

0

런타임은 사용자 코드에 도달 할 수 없을 정도로 될 때까지 내부 참조를 유지하는 것이 여전히 가질 수있다 finalizer는 런타임의 finalization 스레드에서 호출됩니다. 파이널 라이저를 호출 할 때 시간이 중요하다는 점을 감안할 때, 사용자가 큐를 억제하라는 요청을 한 경우 큐에 객체를 보관하는 것은 의미가 없습니다. 내 테스트 CLI 구현에서는 사용자 정의 finalizers가있는 객체의 헤더에 SuppressFinalizer 플래그를 유지합니다. 종료 기 스레드가 큐의 해당 오브젝트에 도달 할 때 플래그가 참이면 종료 자 호출은 생략됩니다. O (N) 대신 GC.SuppressFinalize() O (1)을 호출 할 수 있도록 대기열에서 개체를 제거하지 않습니다. 여기서 N은 할당 된 최종 개체의 수입니다. 지연 제거 정책 나중에).

1

제대로 처리되지 않은 유형을 쉽게 찾을 수 있도록하는 Finalizer에 InvalidOperationException을 발생시킵니다. GC.SuppressFinalize가 호출 된 곳에서 Dispose()가 호출되면 예외가 발생하지 않습니다.

관련 문제