Finalizer is running while its object is still in use

Summary: C # /. NET is supposed to collect garbage. C # has a destructor used to clear resources. What happens when object A is a garbage collection of the same row, I try to clone one of its member variables? Apparently, on multiprocessors, sometimes the garbage collector wins ...

Problem

Today, in training in C #, the teacher showed us some code that contained an error only when running on multiprocessors.

I will summarize to say that sometimes the compiler or JIT screws rise, calling the finalizer of a C # class object before returning from its called method.

The full code provided in the Visual C ++ 2005 documentation will be posted as an โ€œanswerโ€ so as not to ask very big questions, but the main ones are below:

The following class has a hash property that returns a cloned copy of the internal array. At is, the first element of the array has a value of 2. In the destructor, its value is set to zero.

The point is this: if you try to get the "Hash" property in the "Example", you will get a clean copy of the array, whose first element is still 2, because the object is used (and as such, not garbage collection / end):

public class Example
{
    private int nValue;
    public int N { get { return nValue; } }

    // The Hash property is slower because it clones an array. When
    // KeepAlive is not used, the finalizer sometimes runs before 
    // the Hash property value is read.

    private byte[] hashValue;
    public byte[] Hash { get { return (byte[])hashValue.Clone(); } }

    public Example()
    {
        nValue = 2;
        hashValue = new byte[20];
        hashValue[0] = 2;
    }

    ~Example()
    {
        nValue = 0;

        if (hashValue != null)
        {
            Array.Clear(hashValue, 0, hashValue.Length);
        }
    }
}

But everything is so simple ... The code using this class is wokring inside the stream and, of course, for the test, the application is highly multithreaded:

public static void Main(string[] args)
{
    Thread t = new Thread(new ThreadStart(ThreadProc));
    t.Start();
    t.Join();
}

private static void ThreadProc()
{
    // running is a boolean which is always true until
    // the user press ENTER
    while (running) DoWork();
}

The static DoWork method is the code in which the problem occurs:

private static void DoWork()
{
    Example ex = new Example();

    byte[] res = ex.Hash; // [1]

    // If the finalizer runs before the call to the Hash 
    // property completes, the hashValue array might be
    // cleared before the property value is read. The 
    // following test detects that.

    if (res[0] != 2)
    {
        // Oops... The finalizer of ex was launched before
        // the Hash method/property completed
    }
}

1 000 000 DoWork, -, Garbage Collector "ex", , , "". , , , ( 1- 2).

, , , [1] DoWork, :

    // Supposed inlined processing
    byte[] res2 = ex.Hash2;
    // note that after this line, "ex" could be garbage collected,
    // but not res2
    byte[] res = (byte[])res2.Clone();

, Hash2 - , :

// Hash2 code:
public byte[] Hash2 { get { return (byte[])hashValue; } }

, : , #/. NET, JIT?

.

http://blogs.msdn.com/cbrumme/archive/2003/04/19/51365.aspx
http://blogs.msdn.com/clyon/archive/2004/09/21/232445.aspx

, . +1...

: -)

2

Linux/Ubuntu/Mono, , ( , ..)

+3
8

: .

. IDisposable.

"protected Dispose (bool disposing)". , , .

, .

+7

, , .

, , ( ) .

.

, , , , .

, ex- , , , , ( ).

- GC.KeepAlive ex, , :

GC.KeepAlive(ex);

, .NET Framework.

+3

GC; , , :

(1) if, ex.Hash [0] res, ex GC'd

(2) ex Hash

- , JIT, ?

+1

, - , . GC.KeepAlive(), , GC, , .

DoWork " ", , :

byte[] res = ex.Hash;

ex-, . GC.KeepAlive .

+1

, , ex.Hash, CLR , ex- ...

, , :

private static void DoWork()
{
    Example ex = new Example();

    byte[] res = ex.Hash; // [1]

    // If the finalizer runs before the call to the Hash 
    // property completes, the hashValue array might be
    // cleared before the property value is read. The 
    // following test detects that.

    if (res[0] != 2) // NOTE
    {
        // Oops... The finalizer of ex was launched before
        // the Hash method/property completed
    }
  GC.KeepAlive(ex); // keep our instance alive in case we need it.. uh.. we don't
}

GC.KeepAlive ... :) , /jittable , , GC , .

. , DoWork ++... , , . IE. , blob , blob. , GC .

. ...

+1

, , .

, , , , ", null?".

+1

http://blogs.msdn.com/cbrumme/archive/2003/04/19/51365.aspx

class C {<br>
   IntPtr _handle;
   Static void OperateOnHandle(IntPtr h) { ... }
   void m() {
      OperateOnHandle(_handle);
      ...
   }
   ...
}

class Other {
   void work() {
      if (something) {
         C aC = new C();
         aC.m();
         ...  // most guess here
      } else {
         ...
      }
   }
}

, , "aC ". JIT , Other.work(). Other.work() - aC . "aC = null;" JIT . , JIT , GC .

, aC. , , aC , Other.work() s "if", Ive . , . . Other.work() aC, aC.m().

+1

, / Visual ++ 2008.cs. Linux, Mono , . , :

using System;
using System.Threading;

public class Example
{
    private int nValue;
    public int N { get { return nValue; } }

    // The Hash property is slower because it clones an array. When
    // KeepAlive is not used, the finalizer sometimes runs before 
    // the Hash property value is read.

    private byte[] hashValue;
    public byte[] Hash { get { return (byte[])hashValue.Clone(); } }
    public byte[] Hash2 { get { return (byte[])hashValue; } }

    public int returnNothing() { return 25; }

    public Example()
    {
        nValue = 2;
        hashValue = new byte[20];
        hashValue[0] = 2;
    }

    ~Example()
    {
        nValue = 0;

        if (hashValue != null)
        {
            Array.Clear(hashValue, 0, hashValue.Length);
        }
    }
}

public class Test
{
    private static int totalCount = 0;
    private static int finalizerFirstCount = 0;

    // This variable controls the thread that runs the demo.
    private static bool running = true;

    // In order to demonstrate the finalizer running first, the
    // DoWork method must create an Example object and invoke its
    // Hash property. If there are no other calls to members of
    // the Example object in DoWork, garbage collection reclaims
    // the Example object aggressively. Sometimes this means that
    // the finalizer runs before the call to the Hash property
    // completes. 

    private static void DoWork()
    {
        totalCount++;

        // Create an Example object and save the value of the 
        // Hash property. There are no more calls to members of 
        // the object in the DoWork method, so it is available
        // for aggressive garbage collection.

        Example ex = new Example();

        // Normal processing
        byte[] res = ex.Hash;

        // Supposed inlined processing
        //byte[] res2 = ex.Hash2;
        //byte[] res = (byte[])res2.Clone();

        // successful try to keep reference alive
        //ex.returnNothing();

        // Failed try to keep reference alive
        //ex = null;

        // If the finalizer runs before the call to the Hash 
        // property completes, the hashValue array might be
        // cleared before the property value is read. The 
        // following test detects that.

        if (res[0] != 2)
        {
            finalizerFirstCount++;
            Console.WriteLine("The finalizer ran first at {0} iterations.", totalCount);
        }

        //GC.KeepAlive(ex);
    }

    public static void Main(string[] args)
    {
        Console.WriteLine("Test:");

        // Create a thread to run the test.
        Thread t = new Thread(new ThreadStart(ThreadProc));
        t.Start();

        // The thread runs until Enter is pressed.
        Console.WriteLine("Press Enter to stop the program.");
        Console.ReadLine();

        running = false;

        // Wait for the thread to end.
        t.Join();

        Console.WriteLine("{0} iterations total; the finalizer ran first {1} times.", totalCount, finalizerFirstCount);
    }

    private static void ThreadProc()
    {
        while (running) DoWork();
    }
}

For those who are interested, I can send an archived project by e-mail.

0
source

Source: https://habr.com/ru/post/1697525/


All Articles