Revert "Simplified MinOpts GC ref tracking."
authorJan Kotas <jkotas@microsoft.com>
Sat, 25 Feb 2017 14:33:11 +0000 (06:33 -0800)
committerGitHub <noreply@github.com>
Sat, 25 Feb 2017 14:33:11 +0000 (06:33 -0800)
Commit migrated from https://github.com/dotnet/coreclr/commit/a77dae93c1708663e864e77159b16bdaee7c9635

14 files changed:
src/coreclr/src/jit/codegenarm.cpp
src/coreclr/src/jit/codegenarm64.cpp
src/coreclr/src/jit/codegenlegacy.cpp
src/coreclr/src/jit/codegenxarch.cpp
src/coreclr/src/jit/gcencode.cpp
src/coreclr/src/jit/jitconfigvalues.h
src/coreclr/src/jit/jitgcinfo.h
src/coreclr/src/jit/lclvars.cpp
src/coreclr/src/vm/gcinfodecoder.cpp
src/coreclr/tests/src/GC/API/GC/KeepAlive.cs
src/coreclr/tests/src/GC/Scenarios/LeakGen/leakgen.cs
src/coreclr/tests/src/GC/Scenarios/Samples/gc.cs
src/coreclr/tests/src/GC/Scenarios/WeakReference/weakreffinal.cs
src/coreclr/tests/src/JIT/Methodical/Arrays/misc/arrres.cs

index 27d188f..ca6ae01 100644 (file)
@@ -1918,14 +1918,12 @@ void CodeGen::genCreateAndStoreGCInfo(unsigned codeSize,
     // Follow the code pattern of the x86 gc info encoder (genCreateAndStoreGCInfoJIT32).
     gcInfo.gcInfoBlockHdrSave(gcInfoEncoder, codeSize, prologSize);
 
-    // We keep the call count for the second call to gcMakeRegPtrTable() below.
-    unsigned callCnt = 0;
     // First we figure out the encoder ID's for the stack slots and registers.
-    gcInfo.gcMakeRegPtrTable(gcInfoEncoder, codeSize, prologSize, GCInfo::MAKE_REG_PTR_MODE_ASSIGN_SLOTS, &callCnt);
+    gcInfo.gcMakeRegPtrTable(gcInfoEncoder, codeSize, prologSize, GCInfo::MAKE_REG_PTR_MODE_ASSIGN_SLOTS);
     // Now we've requested all the slots we'll need; "finalize" these (make more compact data structures for them).
     gcInfoEncoder->FinalizeSlotIds();
     // Now we can actually use those slot ID's to declare live ranges.
-    gcInfo.gcMakeRegPtrTable(gcInfoEncoder, codeSize, prologSize, GCInfo::MAKE_REG_PTR_MODE_DO_WORK, &callCnt);
+    gcInfo.gcMakeRegPtrTable(gcInfoEncoder, codeSize, prologSize, GCInfo::MAKE_REG_PTR_MODE_DO_WORK);
 
     gcInfoEncoder->Build();
 
index 62ebc48..5c31a09 100644 (file)
@@ -6061,17 +6061,14 @@ void CodeGen::genCreateAndStoreGCInfoX64(unsigned codeSize, unsigned prologSize
     // Follow the code pattern of the x86 gc info encoder (genCreateAndStoreGCInfoJIT32).
     gcInfo.gcInfoBlockHdrSave(gcInfoEncoder, codeSize, prologSize);
 
-    // We keep the call count for the second call to gcMakeRegPtrTable() below.
-    unsigned callCnt = 0;
-
     // First we figure out the encoder ID's for the stack slots and registers.
-    gcInfo.gcMakeRegPtrTable(gcInfoEncoder, codeSize, prologSize, GCInfo::MAKE_REG_PTR_MODE_ASSIGN_SLOTS, &callCnt);
+    gcInfo.gcMakeRegPtrTable(gcInfoEncoder, codeSize, prologSize, GCInfo::MAKE_REG_PTR_MODE_ASSIGN_SLOTS);
 
     // Now we've requested all the slots we'll need; "finalize" these (make more compact data structures for them).
     gcInfoEncoder->FinalizeSlotIds();
 
     // Now we can actually use those slot ID's to declare live ranges.
-    gcInfo.gcMakeRegPtrTable(gcInfoEncoder, codeSize, prologSize, GCInfo::MAKE_REG_PTR_MODE_DO_WORK, &callCnt);
+    gcInfo.gcMakeRegPtrTable(gcInfoEncoder, codeSize, prologSize, GCInfo::MAKE_REG_PTR_MODE_DO_WORK);
 
     if (compiler->opts.compDbgEnC)
     {
index 922a9b1..7fd2956 100644 (file)
@@ -20164,14 +20164,12 @@ void CodeGen::genCreateAndStoreGCInfoX64(unsigned codeSize, unsigned prologSize
     // Follow the code pattern of the x86 gc info encoder (genCreateAndStoreGCInfoJIT32).
     gcInfo.gcInfoBlockHdrSave(gcInfoEncoder, codeSize, prologSize);
 
-    // We keep the call count for the second call to gcMakeRegPtrTable() below.
-    unsigned callCnt = 0;
     // First we figure out the encoder ID's for the stack slots and registers.
-    gcInfo.gcMakeRegPtrTable(gcInfoEncoder, codeSize, prologSize, GCInfo::MAKE_REG_PTR_MODE_ASSIGN_SLOTS, &callCnt);
+    gcInfo.gcMakeRegPtrTable(gcInfoEncoder, codeSize, prologSize, GCInfo::MAKE_REG_PTR_MODE_ASSIGN_SLOTS);
     // Now we've requested all the slots we'll need; "finalize" these (make more compact data structures for them).
     gcInfoEncoder->FinalizeSlotIds();
     // Now we can actually use those slot ID's to declare live ranges.
-    gcInfo.gcMakeRegPtrTable(gcInfoEncoder, codeSize, prologSize, GCInfo::MAKE_REG_PTR_MODE_DO_WORK, &callCnt);
+    gcInfo.gcMakeRegPtrTable(gcInfoEncoder, codeSize, prologSize, GCInfo::MAKE_REG_PTR_MODE_DO_WORK);
 
     gcInfoEncoder->Build();
 
index acf7a0d..66ae5ff 100644 (file)
@@ -8361,14 +8361,12 @@ void CodeGen::genCreateAndStoreGCInfoX64(unsigned codeSize, unsigned prologSize
     // Follow the code pattern of the x86 gc info encoder (genCreateAndStoreGCInfoJIT32).
     gcInfo.gcInfoBlockHdrSave(gcInfoEncoder, codeSize, prologSize);
 
-    // We keep the call count for the second call to gcMakeRegPtrTable() below.
-    unsigned callCnt = 0;
     // First we figure out the encoder ID's for the stack slots and registers.
-    gcInfo.gcMakeRegPtrTable(gcInfoEncoder, codeSize, prologSize, GCInfo::MAKE_REG_PTR_MODE_ASSIGN_SLOTS, &callCnt);
+    gcInfo.gcMakeRegPtrTable(gcInfoEncoder, codeSize, prologSize, GCInfo::MAKE_REG_PTR_MODE_ASSIGN_SLOTS);
     // Now we've requested all the slots we'll need; "finalize" these (make more compact data structures for them).
     gcInfoEncoder->FinalizeSlotIds();
     // Now we can actually use those slot ID's to declare live ranges.
-    gcInfo.gcMakeRegPtrTable(gcInfoEncoder, codeSize, prologSize, GCInfo::MAKE_REG_PTR_MODE_DO_WORK, &callCnt);
+    gcInfo.gcMakeRegPtrTable(gcInfoEncoder, codeSize, prologSize, GCInfo::MAKE_REG_PTR_MODE_DO_WORK);
 
     if (compiler->opts.compDbgEnC)
     {
index 2b9fe37..dcca19e 100644 (file)
@@ -3844,15 +3844,13 @@ struct InterruptibleRangeReporter
     }
 };
 
-void GCInfo::gcMakeRegPtrTable(
-    GcInfoEncoder* gcInfoEncoder, unsigned codeSize, unsigned prologSize, MakeRegPtrMode mode, unsigned* callCntRef)
+void GCInfo::gcMakeRegPtrTable(GcInfoEncoder* gcInfoEncoder,
+                               unsigned       codeSize,
+                               unsigned       prologSize,
+                               MakeRegPtrMode mode)
 {
     GCENCODER_WITH_LOGGING(gcInfoEncoderWithLog, gcInfoEncoder);
 
-    const bool noTrackedGCSlots =
-        (compiler->opts.MinOpts() && !compiler->opts.jitFlags->IsSet(JitFlags::JIT_FLAG_PREJIT) &&
-         !JitConfig.JitMinOptsTrackGCrefs());
-
     if (mode == MAKE_REG_PTR_MODE_ASSIGN_SLOTS)
     {
         m_regSlotMap   = new (compiler->getAllocator()) RegSlotMap(compiler->getAllocator());
@@ -3963,25 +3961,14 @@ void GCInfo::gcMakeRegPtrTable(
             {
                 stackSlotBase = GC_FRAMEREG_REL;
             }
-            if (noTrackedGCSlots)
+            StackSlotIdKey sskey(varDsc->lvStkOffs, (stackSlotBase == GC_FRAMEREG_REL), flags);
+            GcSlotId       varSlotId;
+            if (mode == MAKE_REG_PTR_MODE_ASSIGN_SLOTS)
             {
-                // No need to hash/lookup untracked GC refs; just grab a new Slot Id.
-                if (mode == MAKE_REG_PTR_MODE_ASSIGN_SLOTS)
-                {
-                    gcInfoEncoderWithLog->GetStackSlotId(varDsc->lvStkOffs, flags, stackSlotBase);
-                }
-            }
-            else
-            {
-                StackSlotIdKey sskey(varDsc->lvStkOffs, (stackSlotBase == GC_FRAMEREG_REL), flags);
-                GcSlotId       varSlotId;
-                if (mode == MAKE_REG_PTR_MODE_ASSIGN_SLOTS)
+                if (!m_stackSlotMap->Lookup(sskey, &varSlotId))
                 {
-                    if (!m_stackSlotMap->Lookup(sskey, &varSlotId))
-                    {
-                        varSlotId = gcInfoEncoderWithLog->GetStackSlotId(varDsc->lvStkOffs, flags, stackSlotBase);
-                        m_stackSlotMap->Set(sskey, varSlotId);
-                    }
+                    varSlotId = gcInfoEncoderWithLog->GetStackSlotId(varDsc->lvStkOffs, flags, stackSlotBase);
+                    m_stackSlotMap->Set(sskey, varSlotId);
                 }
             }
         }
@@ -4217,24 +4204,9 @@ void GCInfo::gcMakeRegPtrTable(
         {
             if (gcCallDescList != nullptr)
             {
-                if (noTrackedGCSlots)
+                for (CallDsc* call = gcCallDescList; call != nullptr; call = call->cdNext)
                 {
-                    // We have the call count from the previous run.
-                    numCallSites = *callCntRef;
-
-                    // If there are no calls, tell the world and bail.
-                    if (numCallSites == 0)
-                    {
-                        gcInfoEncoderWithLog->DefineCallSites(nullptr, nullptr, 0);
-                        return;
-                    }
-                }
-                else
-                {
-                    for (CallDsc* call = gcCallDescList; call != nullptr; call = call->cdNext)
-                    {
-                        numCallSites++;
-                    }
+                    numCallSites++;
                 }
                 pCallSites     = new (compiler, CMK_GC) unsigned[numCallSites];
                 pCallSiteSizes = new (compiler, CMK_GC) BYTE[numCallSites];
@@ -4244,8 +4216,17 @@ void GCInfo::gcMakeRegPtrTable(
         // Now consider every call.
         for (CallDsc* call = gcCallDescList; call != nullptr; call = call->cdNext)
         {
+            if (mode == MAKE_REG_PTR_MODE_DO_WORK)
+            {
+                pCallSites[callSiteNum]     = call->cdOffs - call->cdCallInstrSize;
+                pCallSiteSizes[callSiteNum] = call->cdCallInstrSize;
+                callSiteNum++;
+            }
+
+            unsigned nextOffset;
+
             // Figure out the code offset of this entry.
-            unsigned nextOffset = call->cdOffs;
+            nextOffset = call->cdOffs;
 
             // As far as I (DLD, 2010) can determine by asking around, the "call->u1.cdArgMask"
             // and "cdArgCnt" cases are to handle x86 situations in which a call expression is nested as an
@@ -4270,36 +4251,13 @@ void GCInfo::gcMakeRegPtrTable(
             assert(call->cdOffs >= call->cdCallInstrSize);
             // call->cdOffs is actually the offset of the instruction *following* the call, so subtract
             // the call instruction size to get the offset of the actual call instruction...
-            unsigned callOffset = nextOffset - call->cdCallInstrSize;
-
-            if (noTrackedGCSlots && regMask == 0)
-            {
-                // No live GC refs in regs at the call -> don't record the call.
-            }
-            else
-            {
-                // Append an entry for the call if doing the real thing.
-                if (mode == MAKE_REG_PTR_MODE_DO_WORK)
-                {
-                    pCallSites[callSiteNum]     = callOffset;
-                    pCallSiteSizes[callSiteNum] = call->cdCallInstrSize;
-                }
-                callSiteNum++;
-
-                // Record that these registers are live before the call...
-                gcInfoRecordGCRegStateChange(gcInfoEncoder, mode, callOffset, regMask, GC_SLOT_LIVE, byrefRegMask,
-                                             nullptr);
-                // ...and dead after.
-                gcInfoRecordGCRegStateChange(gcInfoEncoder, mode, nextOffset, regMask, GC_SLOT_DEAD, byrefRegMask,
-                                             nullptr);
-            }
+            unsigned callOffset = call->cdOffs - call->cdCallInstrSize;
+            // Record that these registers are live before the call...
+            gcInfoRecordGCRegStateChange(gcInfoEncoder, mode, callOffset, regMask, GC_SLOT_LIVE, byrefRegMask, nullptr);
+            // ...and dead after.
+            gcInfoRecordGCRegStateChange(gcInfoEncoder, mode, call->cdOffs, regMask, GC_SLOT_DEAD, byrefRegMask,
+                                         nullptr);
         }
-
-        // Make sure we've recorded the expected number of calls
-        assert(mode != MAKE_REG_PTR_MODE_DO_WORK || numCallSites == callSiteNum);
-        // Return the actual recorded call count to the caller
-        *callCntRef = callSiteNum;
-
         // OK, define the call sites.
         if (mode == MAKE_REG_PTR_MODE_DO_WORK)
         {
index 264b661..a3e30de 100644 (file)
@@ -214,15 +214,6 @@ CONFIG_INTEGER(JitEnableNoWayAssert, W("JitEnableNoWayAssert"), 0)
 CONFIG_INTEGER(JitEnableNoWayAssert, W("JitEnableNoWayAssert"), 1)
 #endif // !defined(DEBUG) && !defined(_DEBUG)
 
-#if !defined(JIT32_GCENCODER)
-#if defined(_TARGET_AMD64_) && defined(FEATURE_CORECLR)
-#define JitMinOptsTrackGCrefs_Default 0 // Not tracking GC refs in MinOpts is new behavior
-#else
-#define JitMinOptsTrackGCrefs_Default 1
-#endif
-CONFIG_INTEGER(JitMinOptsTrackGCrefs, W("JitMinOptsTrackGCrefs"), JitMinOptsTrackGCrefs_Default) // Track GC roots
-#endif // !defined(JIT32_GCENCODER)
-
 // The following should be wrapped inside "#if MEASURE_MEM_ALLOC / #endif", but
 // some files include this one without bringing in the definitions from "jit.h"
 // so we don't always know what the "true" value of that flag should be. For now
index 7b17b84..3f8d8af 100644 (file)
@@ -295,11 +295,7 @@ public:
     // references, building up mappings from tuples of <reg/offset X byref/pinning> to the corresponding
     // slot id (in the two member fields declared above).  In the "do work" mode, we use these slot ids to
     // actually declare live ranges to the encoder.
-    void gcMakeRegPtrTable(GcInfoEncoder* gcInfoEncoder,
-                           unsigned       codeSize,
-                           unsigned       prologSize,
-                           MakeRegPtrMode mode,
-                           unsigned*      callCntRef);
+    void gcMakeRegPtrTable(GcInfoEncoder* gcInfoEncoder, unsigned codeSize, unsigned prologSize, MakeRegPtrMode mode);
 #endif
 
 #ifdef JIT32_GCENCODER
index 3edff3b..6e5ab9e 100644 (file)
@@ -3023,10 +3023,6 @@ void Compiler::lvaSortByRefCount()
             lvaSetVarDoNotEnregister(lclNum DEBUGARG(DNER_PinningRef));
 #endif
         }
-        else if (opts.MinOpts() && !JitConfig.JitMinOptsTrackGCrefs() && varTypeIsGC(varDsc->TypeGet()))
-        {
-            varDsc->lvTracked = 0;
-        }
 
         //  Are we not optimizing and we have exception handlers?
         //   if so mark all args and locals "do not enregister".
index 5dd52b9..89f4704 100644 (file)
@@ -628,8 +628,6 @@ bool GcInfoDecoder::EnumerateLiveSlots(
 
 
 #ifdef PARTIALLY_INTERRUPTIBLE_GC_SUPPORTED
-    bool noTrackedRefs = false;
-
     if(m_SafePointIndex < m_NumSafePoints && !executionAborted)
     {
         // Skip interruptibility information
@@ -650,40 +648,33 @@ bool GcInfoDecoder::EnumerateLiveSlots(
         //
         if(!executionAborted)
         {
-            if(m_NumInterruptibleRanges == 0)
-            {
-                // No ranges and no explicit safepoint - must be MinOpts with untracked refs.
-                noTrackedRefs = true;
-            }
+            _ASSERTE(m_NumInterruptibleRanges);
         }
 
-        if(m_NumInterruptibleRanges != 0)
+        int countIntersections = 0;
+        UINT32 lastNormStop = 0;
+        for(UINT32 i=0; i<m_NumInterruptibleRanges; i++)
         {
-            int countIntersections = 0;
-            UINT32 lastNormStop = 0;
-            for(UINT32 i=0; i<m_NumInterruptibleRanges; i++)
-            {
-                UINT32 normStartDelta = (UINT32) m_Reader.DecodeVarLengthUnsigned( INTERRUPTIBLE_RANGE_DELTA1_ENCBASE );
-                UINT32 normStopDelta = (UINT32) m_Reader.DecodeVarLengthUnsigned( INTERRUPTIBLE_RANGE_DELTA2_ENCBASE ) + 1;
+            UINT32 normStartDelta = (UINT32) m_Reader.DecodeVarLengthUnsigned( INTERRUPTIBLE_RANGE_DELTA1_ENCBASE );
+            UINT32 normStopDelta = (UINT32) m_Reader.DecodeVarLengthUnsigned( INTERRUPTIBLE_RANGE_DELTA2_ENCBASE ) + 1;
 
-                UINT32 normStart = lastNormStop + normStartDelta;
-                UINT32 normStop = normStart + normStopDelta;
-                if(normBreakOffset >= normStart && normBreakOffset < normStop)
-                {
-                    _ASSERTE(pseudoBreakOffset == 0);
-                    countIntersections++;
-                    pseudoBreakOffset = numInterruptibleLength + normBreakOffset - normStart;
-                }
-                numInterruptibleLength += normStopDelta;
-                lastNormStop = normStop;
-            }        
-            _ASSERTE(countIntersections <= 1);
-            if(countIntersections == 0)
+            UINT32 normStart = lastNormStop + normStartDelta;
+            UINT32 normStop = normStart + normStopDelta;
+            if(normBreakOffset >= normStart && normBreakOffset < normStop)
             {
-                _ASSERTE(executionAborted);
-                LOG((LF_GCROOTS, LL_INFO100000, "Not reporting this frame because it is aborted and not fully interruptible.\n"));
-                goto ExitSuccess;
+                _ASSERTE(pseudoBreakOffset == 0);
+                countIntersections++;
+                pseudoBreakOffset = numInterruptibleLength + normBreakOffset - normStart;
             }
+            numInterruptibleLength += normStopDelta;
+            lastNormStop = normStop;
+        }        
+        _ASSERTE(countIntersections <= 1);
+        if(countIntersections == 0)
+        {
+            _ASSERTE(executionAborted);
+            LOG((LF_GCROOTS, LL_INFO100000, "Not reporting this frame because it is aborted and not fully interruptible.\n"));
+            goto ExitSuccess;
         }
     }        
 #else   // !PARTIALLY_INTERRUPTIBLE_GC_SUPPORTED
@@ -727,7 +718,7 @@ bool GcInfoDecoder::EnumerateLiveSlots(
 
         if(executionAborted)
         {
-            // Skip over safepoint info (if any is present).
+            _ASSERTE(m_NumSafePoints == 0);
             m_Reader.Skip(m_NumSafePoints * numSlots);
         }
         else if( m_SafePointIndex != m_NumSafePoints )
@@ -796,8 +787,6 @@ bool GcInfoDecoder::EnumerateLiveSlots(
         else
         {
             m_Reader.Skip(m_NumSafePoints * numSlots);
-            if(noTrackedRefs)
-                goto ReportUntracked;
         }
 #endif // PARTIALLY_INTERRUPTIBLE_GC_SUPPORTED
         
index 3174f89..df3d9d2 100644 (file)
@@ -47,23 +47,16 @@ public class Test
     }
 
 
-    public static void RunTest2()
-    {
-        Dummy2 obj2 = new Dummy2();
-        obj2 = null;
-    }
-
-
     public static void RunTest()
     {
         Dummy obj = new Dummy();
-
-        RunTest2();
+        Dummy2 obj2 = new Dummy2();
 
         // *uncomment the for loop to make test fail with complus_jitminops set
         // by design as per briansul
 
         //for (int i=0; i<5; i++) {
+        obj2 = null;
         GC.Collect();
         GC.WaitForPendingFinalizers();
         //}
index 8ec6531..9235256 100644 (file)
@@ -62,10 +62,6 @@ namespace LGen {
                 MakeLeak(iObj);
             }
 
-            GC.Collect();
-            GC.WaitForPendingFinalizers();
-            GC.Collect();
-
             Console.WriteLine("~LeakObject() was called {0} times.", LeakObject.icFinal);
             return (LeakObject.icFinal == iObj*iRep);
         }
@@ -84,6 +80,12 @@ namespace LGen {
                 mem[mem.Length-1] = 1;
             }
 
+            Mv_Obj = null;
+
+            GC.Collect();
+            GC.WaitForPendingFinalizers();
+            GC.Collect();
+
         }
 
 
index 7ed11bc..a6c1cfe 100644 (file)
@@ -273,21 +273,16 @@ class Application {
     static public ResurrectObj ResObjHolder;    // Defaults to null
 
 
-    // These methods demonstrate how the GC supports resurrection.
+    // This method demonstrates how the GC supports resurrection.
     // NOTE: Resurrection is discouraged.
-    private static void ResurrectionInit() {
+    private static void ResurrectionDemo() {
+        Display(0, "\n\nDemo start: Object Resurrection.", +1);
+
         // Create a ResurrectionObj
         ResurrectObj obj = new ResurrectObj("Resurrection");
 
         // Destroy all strong references to the new ResurrectionObj
         obj = null;
-    }
-
-    private static void ResurrectionDemo() {
-        Display(0, "\n\nDemo start: Object Resurrection.", +1);
-
-        // Create a ResurrectionObj and drop it on the floor.
-        ResurrectionInit();
 
         // Force the GC to determine that the object is unreachable.
         Collect();
index 754e88d..a356cec 100644 (file)
@@ -22,11 +22,6 @@ namespace DefaultNamespace {
         public bool RunTest(int iObj,int iSwitch)
         {
             DeleteObj(iObj,iSwitch);
-
-            GC.Collect();
-            GC.WaitForPendingFinalizers();
-            GC.Collect();
-
             bool result = CheckResult(iObj,iSwitch);
             return result;
         }
@@ -53,6 +48,11 @@ namespace DefaultNamespace {
             {
                 rgNode[i] = null;
             }
+
+            GC.Collect();
+            GC.WaitForPendingFinalizers();
+            GC.Collect();
+
         }
 
         public bool CheckResult(int iObj,int iSwitch)
index e021a59..7956cc6 100644 (file)
@@ -45,18 +45,13 @@ namespace GCTest
             return 100;
         }
         [System.Runtime.CompilerServices.MethodImplAttribute(System.Runtime.CompilerServices.MethodImplOptions.NoInlining)]
-        private static void CollectAndFinalize()
-        {
-            GC.Collect();
-            GC.WaitForPendingFinalizers();
-            GC.Collect();
-        }
-        [System.Runtime.CompilerServices.MethodImplAttribute(System.Runtime.CompilerServices.MethodImplOptions.NoInlining)]
         private static void Test1()
         {
             for (int i = 0; i < 50; i++)
                 s_arr[i] = new Test(i);
-            CollectAndFinalize();
+            GC.Collect();
+            GC.WaitForPendingFinalizers();
+            GC.Collect();
         }
         [System.Runtime.CompilerServices.MethodImplAttribute(System.Runtime.CompilerServices.MethodImplOptions.NoInlining)]
         private static void Test2()
@@ -67,44 +62,52 @@ namespace GCTest
                 s_arr[i].CheckValid();
                 s_arr[i] = null;
             }
+            GC.Collect();
+            GC.WaitForPendingFinalizers();
+            GC.Collect();
         }
         [System.Runtime.CompilerServices.MethodImplAttribute(System.Runtime.CompilerServices.MethodImplOptions.NoInlining)]
         private static void Test3()
         {
-            CollectAndFinalize();
             for (int i = 0; i < 50; i++)
             {
                 if (s_arr[i] == null) throw new Exception();
                 s_arr[i].CheckValid();
                 s_arr[i] = null;
             }
+            GC.Collect();
+            GC.WaitForPendingFinalizers();
+            GC.Collect();
         }
         [System.Runtime.CompilerServices.MethodImplAttribute(System.Runtime.CompilerServices.MethodImplOptions.NoInlining)]
         private static void Test4()
         {
-            CollectAndFinalize();
             for (int i = 0; i < 50; i++)
             {
                 if (s_arr[i] == null) throw new Exception();
                 s_arr[i].CheckValid();
                 s_arr[i] = null;
             }
+            GC.Collect();
+            GC.WaitForPendingFinalizers();
+            GC.Collect();
         }
         [System.Runtime.CompilerServices.MethodImplAttribute(System.Runtime.CompilerServices.MethodImplOptions.NoInlining)]
         private static void Test5()
         {
-            CollectAndFinalize();
             for (int i = 0; i < 50; i++)
             {
                 if (s_arr[i] == null) throw new Exception();
                 s_arr[i].CheckValid();
                 s_arr[i] = null;
             }
+            GC.Collect();
+            GC.WaitForPendingFinalizers();
+            GC.Collect();
         }
         [System.Runtime.CompilerServices.MethodImplAttribute(System.Runtime.CompilerServices.MethodImplOptions.NoInlining)]
         private static void Test6()
         {
-            CollectAndFinalize();
             for (int i = 0; i < 50; i++)
             {
                 if (s_arr[i] == null) throw new Exception();