none
Can the use of a private accessor cause the underlying test object to finalize twice? RRS feed

  • Question

  • I implemented a C# class (MyClass) with a destructor.  I used the Visual Studio 2008 IDE to create a unit test project.  In my TestInitialize method, I create my object, pass it into a new PrivateObject and pass that into the constructor of a new private accessor (the generated MyClass_Accessor type), which I keep as a member of my unit test class. My individual tests manipulate and test the member private accessor object.  I then have a TestCleanUp method that sets the member private accessor reference to null, calls GC.Collect(), then calls GC.WaitForPendingFinalizers().  I do this, because MyClass adds and removes itself from static containers.  I then have two strange behaviors:

    1) The destructor of my class is called, even though a static container still holds a reference to it.  How can this be?

    2) After calling GC.WaitForPendingFinalizers() at the end of the second unit test, the destructor of first test's target object is executed again!

    I don't know how private accessors behave.  Could the private accessor's finalization be invoking the finalization of my test object, even though other references to it exist?

    Please only respond with information about private accessor behavior or insight as to why I might be encountering this issue.

    Please do not respond with a lecture on using Dispose instead.  I have already realized that I should go that route.  I just hate walking away from an issue without understanding why it is happening.

    Thanks!
    • Moved by Figo Fei Monday, January 25, 2010 8:22 AM (From:Visual Studio Team System - Testing)
    Friday, January 15, 2010 11:40 PM

Answers

  •  

    Hi,

    I received your file, thanks.

    With your code, I reproduced this issue and found the root cause, it is caused by the finalize method of MyAppenderClass_Accessor class, you may add "GC.SuppressFinalize(this);" into MyAppenderClass' Finalize method to prevent this issue.

     

     

    For your information, I post an explanation about why the issue happens:

     

    To access private code of MyAppenderClass, MyAppenderClass_Accessor is auto-generated by VS, and the corresponding assembly file is MyAssembly_Accessor.dll. Open this dll with Reflector, navigate to the Finalize method of MyAppenderClass_Accessor class, you will see the method implementation:

     

    [Shadowing("Finalize@0")]

    public override void Finalize()

    {

        Type[] parameterTypes = new Type[0];

        object[] args = new object[0];

        base.m_privateObject.Invoke("Finalize", parameterTypes, args);

    }

     

    From this implementation, we can see that MyAppenderClass_Accessor object will invoke its privateObject's Finalize method when MyAppenderClass_Accessor object's Finalize method is called.

     

    Back to the test project, since we have set a MyAppenderClass object to m_accessor (of type MyAppenderClass_Accessor), 

     

            [TestInitialize()]

            public void Initialize()

            {

                m_log.InfoFormat("{0}() Initializing...", testContextInstance.TestName);

               

                MyAppenderClass target = new MyAppenderClass();

     

                PrivateObject privateObject = new PrivateObject(target);

                m_accessor = new MyAppenderClass_Accessor(privateObject);

     

                Console.WriteLine("{0}() Testing...", testContextInstance.TestName);

            }

     

    MyAppenderClass's Finalize method will be definitely invoked when m_accessor is going to be finalized; On the other hand, when MyAppenderClass has no GC root, its Finalize method will be executed by GC. That's why the MyAppenderClass object finalize twice.

     

    As an evidence, you may set a breakpoint at the beginning of MyAppenderClass' Finalize method, when breakpoint hit, input some sos commands (!clrstack) in the immediate window, and you will find difference between twice call from their callstacks:

     

    .load sos

    extension C:\Windows\Microsoft.NET\Framework\v2.0.50727\sos.dll loaded

     

    // One callstack

    !clrstack

    OS Thread Id: 0xecc (3788)

    ESP       EIP    

    00f1ef74 04c1dc75 MyNamespace.MyAppenderClass.Finalize()

    00f1f5f8 03fa4cc7 Microsoft.VisualStudio.TestTools.UnitTesting.PrivateObject.Invoke(System.String, System.Type[], System.Object[])

    00f1f5f8 03fa4cc7 Microsoft.VisualStudio.TestTools.UnitTesting.PrivateObject.Invoke(System.String, System.Type[], System.Object[])

    00f1f628 04c1dc29 MyNamespace.MyAppenderClass_Accessor.Finalize()

    00f1f8ec 5ae0e2d2 [ContextTransitionFrame: 00f1f8ec]

    00f1f9bc 5ae0e2d2 [GCFrame: 00f1f9bc]

     

    // Another callstack, a different callstack.

    !clrstack

    OS Thread Id: 0xecc (3788)

    ESP       EIP    

    00f1f5f4 04c1dc75 MyNamespace.MyAppenderClass.Finalize()

    00f1f8ec 5ae0e2d2 [ContextTransitionFrame: 00f1f8ec]

    00f1f9bc 5ae0e2d2 [GCFrame: 00f1f9bc]

     


    Sincerely,
    Eric
    MSDN Subscriber Support in Forum
    If you have any feedback of our support, please contact msdnmg@microsoft.com.
    Please remember to mark the replies as answers if they help and unmark them if they provide no help.
    Welcome to the All-In-One Code Framework! If you have any feedback, please tell us.
    • Marked as answer by Josh..Davis Thursday, February 18, 2010 8:06 PM
    Thursday, February 18, 2010 10:22 AM

All replies

  • Hi Josh

    It sounds rather related to GC than the VS IDE Unit test generator.
    Could you clarify a bit about the program and the test code you used, so that we can have a clearer picture of the situation and investigate it further.
    By the way, there is no destructor in C# as it is in C++, we just call it finalizer instead, If an object implements a finalizer and has not disabled finalization by calling SuppressFinalize, it queues in a finalizer list ready for finalization.

    Thanks for understanding.

    Figo Fei
    MSDN Subscriber Support in Forum
    If you have any feedback on our support, please contact msdnmg@microsoft.com

    Please remember to mark the replies as answers if they help and unmark them if they provide no help.
    Welcome to the All-In-One Code Framework! If you have any feedback, please tell us.
    • Edited by Figo Fei Friday, January 29, 2010 1:14 AM
    Monday, January 18, 2010 7:51 AM
  • I understand the basics of finalization where the GC puts the object in a finalization list.  I merely called it a 'destructor', because that is what C# calls it.

    I have a class that specializes log4net's AppenderSkeleton class.  My class constructor gets a named log and adds iteslf to that log's logger's container of appenders.  Like this:

    ILog log = LogManager.GetLogger(LogName);
    Logger logger = (Logger)log.Logger;
    logger.AddAppender(this);

    My class destructor has similar code to remove itself from the container:

    ILog log = LogManager.GetLogger(LogName);
    Logger logger = (Logger)log.Logger;
    logger.RemoveAppender(this);

    The AddAppender and RemoveAppender methods above add to and remove from a container.  My unit tests confirm my test object instance is in the container all the way up to the point where I null my only reference to my test accessor, then call GC.Collect() and GC.WaitForPendingFinalizers() at the end of my first test.  My class' destructor is then called.  I realize I have a design flaw and plan to change my design to implement IDisposable instead.  The question still remains, why did my destructor get called, if a valid reference to my object still exists (in the logger's container of appenders)?  My destructor removes itself from the container, seemingly elminating the last referene to it.  At the end of my second test, I again call GC.Collect() and GC.WaitForPendingFinalizers().  My class destructor (for the object in test 1) gets called a second time.  It attempts to remove itself from the appenders container, which throws an exception.

    So, I have two questions:

    1) How did my destructor get called when a valid reference to it remained?  Is this somehow related to the use of the unit test's private accessor class?

    2) If already called once, why did my destructor get called a second time?
    Monday, January 18, 2010 3:13 PM
  • Hi Josh

    Can you reproduce the problem if the code is not run in a unit test?

    As far as I know, !gcroot in SoS can verify if an object is still rooted.
    You may see more information at http://blogs.msdn.com/dougste/archive/2005/11/25/497016.aspx and http://blogs.msdn.com/delay/archive/2009/03/11/where-s-your-leak-at-using-windbg-sos-and-gcroot-to-diagnose-a-net-memory-leak.aspx.

    Thanks.

    Figo Fei
    MSDN Subscriber Support in Forum
    If you have any feedback on our support, please contact msdnmg@microsoft.com 

    Please remember to mark the replies as answers if they help and unmark them if they provide no help.
    Welcome to the All-In-One Code Framework! If you have any feedback, please tell us.
    Tuesday, January 19, 2010 8:13 AM
  • I did not try without the use of the private accessor.  I have since moved the code from the destructor into a Dispose() method.  All works well.  If I get time, I may get a copy of the original source and see what happens without the private accessor.
    Wednesday, January 20, 2010 3:30 PM
  • Thanks for your info, Josh.
    I guess the reason of two times the code in finalizer got called, is an artificial call by GC.Collect and the other is the automatic call. 

    Thanks.
     

    Figo Fei
    MSDN Subscriber Support in Forum
    If you have any feedback on our support, please contact msdnmg@microsoft.com 

     


    Please remember to mark the replies as answers if they help and unmark them if they provide no help.
    Welcome to the All-In-One Code Framework! If you have any feedback, please tell us.
     
    Thursday, January 21, 2010 1:50 AM
  • So, if a developer calls GC.Collect(), it will execute and run finalizers.  Then, the runtime may call those finalizers again?  That seems bad and basically means a developer can never safely call GC.Collect().
    Friday, January 22, 2010 3:56 PM
  • I meant that could be two different objects of the same finalizer code.

    Move to CLR forum for better answer.

    Thanks.

    Figo Fei
    MSDN Subscriber Support in Forum
    If you have any feedback on our support, please contact msdnmg@microsoft.com

    Please remember to mark the replies as answers if they help and unmark them if they provide no help.
    Welcome to the All-In-One Code Framework! If you have any feedback, please tell us.
    Monday, January 25, 2010 8:21 AM
  • I have a class (MyClass) that specializes log4net's AppenderSkeleton class.  MyClass' constructor gets a named log and adds iteslf to that log's logger's container of appenders.  Like this:

    ILog log = LogManager.GetLogger(LogName);
    Logger logger = (Logger)log.Logger;
    logger.AddAppender(this);

    MyClass' destructor has similar code to remove itself from the container:

    ILog log = LogManager.GetLogger(LogName);
    Logger logger = (Logger)log.Logger;
    logger.RemoveAppender(this);

    The AddAppender and RemoveAppender methods above add to and remove from a container.

    My unit test [TestInitialize()] method create my target object to test, then pass it to a new private accessor (eg. MyClass_Accessor) via a PrivateObject.  This private accessor is a unit test class member.  My unit tests confirm my wrapped test object instance is in the logger's container throughout the test.  My [TestCleanUp()] method nulls my member reference to the private accessor, then calls GC.Collect() followed by GC.WaitForPendingFinalizers().  This causes MyClass' destructor to be called, where my object removes itself from the logger's container.

    I realized I had a design flaw and have since replaced my finalizer with implementing IDisposable.

    However, why did my destructor get called? A valid reference to my object still exists in the logger's container of appenders.

    The strangeness continues...  At the end of my second test, my [TestCleanUp()] method executes again.  MyClass' finalizer is called again.  However, it is for the MyClass object used in the first test!  The finalizer is executing a second time for that object.  I found this because the container throws an exception when the object calls RemoveAppender(this), because 'this' is not in the container.  I added logging of GetHashCode() (which I do not override) and confirmed that the finalizer being executed is for the MyClass object from the first test.  If I use the debugger to skip over the RemoveAppender(this) call, the finalizer is called again, this time for the second test's MyClass object.

    Why is the finalizer getting called twice?

    Monday, January 25, 2010 3:22 PM
  • 1) Do you have a small repro for this? (ideally 10-20 lines of code).
    2) Is the finalizer really called twice, or Dispose method is called twice? (Check out the call stacks, add additional logging, etc.)
    3) The Logger object can have only WeakReference to your object, that would explain why finalizer can be called while Logger still has the object 'registered'.
    4) Was your object by any chance resurrected a re-registered for finalization? (That is not a good idea to do, but it would explain the behavior)
    5) What MyClass::GetHashCode does? Is it really unique? If you are not sure, just add unique ID to each MyClass object.

    -Karel

    Monday, January 25, 2010 5:29 PM
    Moderator
  • 1) No.  My code really is as simple as described.  If I get time, I will go back and re-create the issue with as minimal of code as possible.  I did forget to say that it is in C#.

    2) My question is regarding my initial approach where MyClass did not implement IDisposable or any Dispose() method.  The logging statements executed were in my finalizer (destructor).

    3) The log4net documentation does not say anything about using a weak reference.  However, I do not see why in the world they would hold a registered object as a weak reference.  It needs to be used over and over again and log4net could not re-create it, if it were lost.

    4) I definitely did not implement any code to resurrect or re-register the object.  Unless the private accessor is doing this, it isn't happening.

    5) As I said, I did not implement GetHashCode().  So, it is the one provided by Object.  I log the value when I isntantiate the target MyClass object. I  log it at various points throughout the test and also in the finalizer.  When the first unit test enters the [TestCleanUp()] method, only one instance of a MyClass object have been created.  There is no confusion over which object is being finalized.

    It seems like this must be some weirdness with the private accessor object.  However, I didn't get much help when I posted this problem on the testing form.  A moderator there said to post in this forum.
    Monday, January 25, 2010 11:05 PM
  • I don't quite understand what you mean by private accessor (is it a standard .NET object, or your implementation via Reflection?).
    If you can provide some reasonably-sized repro, that would be probably best.

    -Karel

    Tuesday, January 26, 2010 3:27 AM
    Moderator
  •  

    Hi,

    GC.Collect() method forces an immediate garbage collection of all generations, since the GC operation is expensive and may hits application performance, it is not suggested to invoked manually. However, it doesn't means that we cannot call GC.Collect() in our code.

     

    To answer your concern, consider a reference hierarchy like this:

        Root -> objA -> objB -> objC

    if objA is set to null, objC is going to be collected even though it is reference by objB;

    If objC has a finalizer like:

        // Some logic.

        Root.SomeProperty = this;

        // ...

    After the finalizer got executed, objC becomes accessible again, in another words, objC resurrects, and its finalizer method will be invoked again in the further.

     

    Nevertheless, above scenario is only a guess, could you provide a small project which can reproduce this issue?  it will be helpful to find out what happened.


    Sincerely,
    Eric

    Please remember to mark helpful replies as answers.
    Tuesday, January 26, 2010 3:33 AM
  • I realize callilng GC.Collect() is not recommended due to performance reasons.  However, I am calling it at the end of each unit test, not in release code.

    A private accessor is a class created for you by the Visual Studio 2008 unit test framework, when you choose to do a new unit test for your assembly.  A new unit test project is created with a test class.  A private accessor version of your class is also created.  I'm no expert on the private accessor implementation.  I know that it has a 'Target' property through which you can accessor your actual class.  Otherwise, the private accessor appears to just wrap your class and provide access to its private data.  There is no viewable source code created for the private accessor.  It is behind the scenes magic.  This is why I first posted this thread in the forum on unit testing.

    Perhaps we have Root -> PrivateAccessor obj -> MyClass-obj.  My unit test class has only a reference to the private accessor object.  I set that to null, then call GC.Collect() and GC.WaitForPendingFinalizers().  MyClass obj is finalized as expected.  I don't know what would cause it to get resurrected though.  Nothing in MyClassFinalizer stores a reference to 'this' anywhere.

    I will try to re-create my example code.  Hopefully, I'll get time to do that in the next day or so.
    Tuesday, January 26, 2010 4:44 PM
  • I was easily able to get my code back into the state where it produces this finalize twice problem.  I trimmed it down to remove as much irrelevant code as possible.  The solution has two source files, MyClass.cs (126 lines long) and MyAppenderClassTest.cs (194 lines long).  Each is in its own project where the latter is a unit test project with a private accessor defined for the other project.  This code was developed using Visual Studio 2008 (C#).

    It would be simple, if I could upload my zipped solution (261 KB).  However, I don't see any mechanism to do so.  I'll list portions of the source below:

        public sealed class MyAppenderClass : AppenderSkeleton
        {
            public MyAppenderClass()
            {
                m_log.InfoFormat("Constructing, this = {0}", this.GetHashCode());
                ILog log = LogManager.GetLogger(LogName);
                Logger logger = (Logger)log.Logger;
                logger.AddAppender(this);
                m_log.InfoFormat("Constructed and registered as an appender for the \"{0}\" log", LogName);
            }
    
            ~MyAppenderClass()
            {
                m_log.InfoFormat("Destructing, this = {0}", this.GetHashCode());
    
                ILog log = LogManager.GetLogger(LogName);
                Logger logger = (Logger)log.Logger;
    
                foreach (IAppender appender in logger.Appenders)
                {
                    m_log.InfoFormat("    logger has appender = {0}", appender.GetHashCode());
                }
    
                logger.RemoveAppender(this);
    
                m_log.InfoFormat("Destructed - unregistered as an appender for the \"{0}\" log", LogName);
            }
        public sealed class MyAppenderClassTest
        {
            private MyAppenderClass_Accessor m_accessor;
    
            [TestInitialize()]
            public void Initialize()
            {
                m_log.InfoFormat("{0}() Initializing...", testContextInstance.TestName);
                MyAppenderClass target = new MyAppenderClass();
                m_log.InfoFormat("target = {0}", target.GetHashCode());
                PrivateObject privateObject = new PrivateObject(target);
                m_accessor = new MyAppenderClass_Accessor(privateObject);
                m_log.InfoFormat("{0}() Testing...", testContextInstance.TestName);
    
            }
    
            [TestCleanup()]
            public void CleanUp()
            {
                m_log.InfoFormat("{0}() cleanup - start", testContextInstance.TestName);
                m_accessor = null;
                GC.Collect();
                GC.WaitForPendingFinalizers();
                m_log.InfoFormat("{0}() cleanup - end{1}", testContextInstance.TestName, Environment.NewLine);
            }
    
            [TestMethod()]
            public void Test1()
            {
                VerifyTargetIsMyLogAppender();
                Assert.AreEqual(0, m_accessor.m_messages.Count);
            }
    
            [TestMethod()]
            public void Test2()
            {
                VerifyTargetIsMyLogAppender();
                Assert.AreEqual(0, m_accessor.m_messages.Count);
            }

    Executing the tests produces the following log file:

    INFO UnitTest.MyAppenderClassTest - Test1() Initializing...
    INFO MyNamespace.MyAppenderClass - Constructing, this = 45540859
    INFO MyNamespace.MyAppenderClass - Constructed and registered as an appender for the "MyLog" log
    INFO UnitTest.MyAppenderClassTest - target = 45540859
    INFO UnitTest.MyAppenderClassTest - Test1() Testing...
    INFO UnitTest.MyAppenderClassTest - Test1() cleanup - start
    INFO MyNamespace.MyAppenderClass - Destructing, this = 45540859
    INFO MyNamespace.MyAppenderClass -     logger has appender = 45540859
    INFO MyNamespace.MyAppenderClass - Destructed - unregistered as an appender for the "MyLog" log
    INFO UnitTest.MyAppenderClassTest - Test1() cleanup - end

    INFO UnitTest.MyAppenderClassTest - Test2() Initializing...
    INFO MyNamespace.MyAppenderClass - Constructing, this = 48288863
    INFO MyNamespace.MyAppenderClass - Constructed and registered as an appender for the "MyLog" log
    INFO UnitTest.MyAppenderClassTest - target = 48288863
    INFO UnitTest.MyAppenderClassTest - Test2() Testing...
    INFO UnitTest.MyAppenderClassTest - Test2() cleanup - start
    INFO MyNamespace.MyAppenderClass - Destructing, this = 45540859
    INFO MyNamespace.MyAppenderClass -     logger has appender = 48288863

    As you can see, the MyAppenderClass destructor is called at the end of the first test, even though the log4net Logger's still had a reference to the object in its container of appenders.  Further, at the end of the second test, when the MyAppenderClass destructor is called, it is for the object from Test1.  The finalizer is called a second time.

    Tuesday, January 26, 2010 10:13 PM
  •  

    Hi,

    Sorry to reply so late.

    Your code snippet looks well, I cannot see why MyAppenderClass's finalize method get invoked twice, however, it will be helpful if you can send me your project which can reproduce this issue, please let me know your email address by sending a mail to v-eryang@microsoft.com. Then I will create a file transfer workspace where you can upload your project file. The file will be kept confidential.


    Sincerely,
    Eric

    Please remember to mark helpful replies as answers.
    Thursday, January 28, 2010 6:45 AM
  • Hi Josh,
    I'm writing to check the issue status, please feel free to let us know if you have any concerns.
    Sincerely,
    Eric

    Please remember to mark helpful replies as answers.
    Monday, February 1, 2010 1:33 AM
  • I sent you an email.
    Thursday, February 11, 2010 8:40 PM
  • Hi,
    I'm sorry, but I cannot find your mail in my mailbox, did you send it today, what's the mail title? thank you.
    Sincerely,
    Eric
    MSDN Subscriber Support in Forum
    If you have any feedback of our support, please contact msdnmg@microsoft.com.
    Please remember to mark the replies as answers if they help and unmark them if they provide no help.
    Welcome to the All-In-One Code Framework! If you have any feedback, please tell us.
    Friday, February 12, 2010 6:05 AM
  • I sent the original email on Thursday, Feb. 11.  I just sent it again.  The subject line is "Regarding MSDN Forum Thread on Finalize Twice" without the quotes.
    Tuesday, February 16, 2010 10:18 PM
  • Hi Josh,
    Thanks, I received your mail.
    I created a file transfer workspace, please check your mailbox for the replies.

    Sincerely,
    Eric
    MSDN Subscriber Support in Forum
    If you have any feedback of our support, please contact msdnmg@microsoft.com.
    Please remember to mark the replies as answers if they help and unmark them if they provide no help.
    Welcome to the All-In-One Code Framework! If you have any feedback, please tell us.
    Wednesday, February 17, 2010 2:39 AM
  • I received your email and uploaded my files as a .zip file.  The example depends on log4net.  We use an unaltered log4net version 1.2.10.

    Josh
    Wednesday, February 17, 2010 2:44 PM
  •  

    Hi,

    I received your file, thanks.

    With your code, I reproduced this issue and found the root cause, it is caused by the finalize method of MyAppenderClass_Accessor class, you may add "GC.SuppressFinalize(this);" into MyAppenderClass' Finalize method to prevent this issue.

     

     

    For your information, I post an explanation about why the issue happens:

     

    To access private code of MyAppenderClass, MyAppenderClass_Accessor is auto-generated by VS, and the corresponding assembly file is MyAssembly_Accessor.dll. Open this dll with Reflector, navigate to the Finalize method of MyAppenderClass_Accessor class, you will see the method implementation:

     

    [Shadowing("Finalize@0")]

    public override void Finalize()

    {

        Type[] parameterTypes = new Type[0];

        object[] args = new object[0];

        base.m_privateObject.Invoke("Finalize", parameterTypes, args);

    }

     

    From this implementation, we can see that MyAppenderClass_Accessor object will invoke its privateObject's Finalize method when MyAppenderClass_Accessor object's Finalize method is called.

     

    Back to the test project, since we have set a MyAppenderClass object to m_accessor (of type MyAppenderClass_Accessor), 

     

            [TestInitialize()]

            public void Initialize()

            {

                m_log.InfoFormat("{0}() Initializing...", testContextInstance.TestName);

               

                MyAppenderClass target = new MyAppenderClass();

     

                PrivateObject privateObject = new PrivateObject(target);

                m_accessor = new MyAppenderClass_Accessor(privateObject);

     

                Console.WriteLine("{0}() Testing...", testContextInstance.TestName);

            }

     

    MyAppenderClass's Finalize method will be definitely invoked when m_accessor is going to be finalized; On the other hand, when MyAppenderClass has no GC root, its Finalize method will be executed by GC. That's why the MyAppenderClass object finalize twice.

     

    As an evidence, you may set a breakpoint at the beginning of MyAppenderClass' Finalize method, when breakpoint hit, input some sos commands (!clrstack) in the immediate window, and you will find difference between twice call from their callstacks:

     

    .load sos

    extension C:\Windows\Microsoft.NET\Framework\v2.0.50727\sos.dll loaded

     

    // One callstack

    !clrstack

    OS Thread Id: 0xecc (3788)

    ESP       EIP    

    00f1ef74 04c1dc75 MyNamespace.MyAppenderClass.Finalize()

    00f1f5f8 03fa4cc7 Microsoft.VisualStudio.TestTools.UnitTesting.PrivateObject.Invoke(System.String, System.Type[], System.Object[])

    00f1f5f8 03fa4cc7 Microsoft.VisualStudio.TestTools.UnitTesting.PrivateObject.Invoke(System.String, System.Type[], System.Object[])

    00f1f628 04c1dc29 MyNamespace.MyAppenderClass_Accessor.Finalize()

    00f1f8ec 5ae0e2d2 [ContextTransitionFrame: 00f1f8ec]

    00f1f9bc 5ae0e2d2 [GCFrame: 00f1f9bc]

     

    // Another callstack, a different callstack.

    !clrstack

    OS Thread Id: 0xecc (3788)

    ESP       EIP    

    00f1f5f4 04c1dc75 MyNamespace.MyAppenderClass.Finalize()

    00f1f8ec 5ae0e2d2 [ContextTransitionFrame: 00f1f8ec]

    00f1f9bc 5ae0e2d2 [GCFrame: 00f1f9bc]

     


    Sincerely,
    Eric
    MSDN Subscriber Support in Forum
    If you have any feedback of our support, please contact msdnmg@microsoft.com.
    Please remember to mark the replies as answers if they help and unmark them if they provide no help.
    Welcome to the All-In-One Code Framework! If you have any feedback, please tell us.
    • Marked as answer by Josh..Davis Thursday, February 18, 2010 8:06 PM
    Thursday, February 18, 2010 10:22 AM
  • Thank you for looking into this issue.  It seems odd that the private accessor is calling the wrapped object's finalizer.  This basically means that any class that has a private accessor and implements a finalizer must put GC.SuppressFinalize(this) in its finalizer, something you normally wouldn't do.

    Thursday, February 18, 2010 8:06 PM
  • Hi Josh

    Can you reproduce the problem if thecode is not run in a unit test?

    As far as I know, !gcroot in SoS can verify if an object is still rooted.
    You may see more information at http://blogs.msdn.com/dougste/archive/2005/11/25/497016.aspx and http://blogs.msdn.com/delay/archive/2009/03/11/where-s-your-leak-at-using-windbg-sos-and-gcroot-to-diagnose-a-net-memory-leak.aspx.

    Thanks.

    Figo Fei
    MSDN Subscriber Support in Forum
    If you have any feedback on our support, please contact msdnmg@microsoft.com 

    Please remember to mark the replies as answers if they help and unmark them if they provide no help.
    Welcome to the All-In-One Code Framework! If you have any feedback, please tell us.

    The link cannot be opened, Is the link invalid?
    Thursday, August 19, 2010 1:58 AM