diff --git a/Tests/NFUnitTestGC/TestGC.cs b/Tests/NFUnitTestGC/TestGC.cs
index 0bec97a..bf52e12 100644
--- a/Tests/NFUnitTestGC/TestGC.cs
+++ b/Tests/NFUnitTestGC/TestGC.cs
@@ -1,7 +1,8 @@
-// Licensed to the .NET Foundation under one or more agreements.
+// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
+using System;
using nanoFramework.TestFramework;
namespace NFUnitTestGC
@@ -15,8 +16,6 @@ public void TestGCStress()
int maxArraySize = 1024 * 32;
object[] arrays = new object[600];
- // Starting TestGCStress
-
for (int loop = 0; loop < 100; loop++)
{
OutputHelper.WriteLine($"Running iteration {loop}");
@@ -24,7 +23,7 @@ public void TestGCStress()
for (int i = 0; i < arrays.Length - 1;)
{
OutputHelper.WriteLine($"Alloc array of {maxArraySize} bytes @ pos {i}");
- arrays[i++] = new byte[maxArraySize]; ;
+ arrays[i++] = new byte[maxArraySize];
OutputHelper.WriteLine($"Alloc array of 64 bytes @ pos {i}");
arrays[i++] = new byte[64];
@@ -37,8 +36,35 @@ public void TestGCStress()
arrays[i] = null;
}
}
+ }
+
+ [TestMethod]
+ public void TestGetTotalMemory()
+ {
+ // create several objects
+ object[] objects = new object[100];
+
+ for (int i = 0; i < objects.Length; i++)
+ {
+ objects[i] = new object();
+ }
+
+ // get total memory
+ long totalMemory = GC.GetTotalMemory(false);
+ OutputHelper.WriteLine($"Total memory: {totalMemory} bytes");
+
+ // release objects
+ for (int i = 0; i < objects.Length; i++)
+ {
+ objects[i] = null;
+ }
+
+ // get total memory, forcing full collection
+ long totalMemoryAfterCollection = GC.GetTotalMemory(true);
+ OutputHelper.WriteLine($"Total memory: {totalMemoryAfterCollection} bytes");
- // Completed TestGCStress
+ // check if memory was released
+ Assert.IsTrue(totalMemory > totalMemoryAfterCollection, "Memory was not released");
}
}
}
diff --git a/Tests/NFUnitTestSystemLib/UnitTestGCTest.cs b/Tests/NFUnitTestSystemLib/UnitTestGCTest.cs
index 05c7364..42a3e8f 100644
--- a/Tests/NFUnitTestSystemLib/UnitTestGCTest.cs
+++ b/Tests/NFUnitTestSystemLib/UnitTestGCTest.cs
@@ -9,6 +9,11 @@ namespace NFUnitTestSystemLib
[TestClass]
public class UnitTestGCTest
{
+#pragma warning disable S1215 // this is intended to test the GC
+#pragma warning disable S1854 // this is intended to test the GC
+#pragma warning disable S2696 // this is intended to test the GC
+#pragma warning disable S3971 // this is intended to test the GC
+
internal class FinalizeObject
{
public static FinalizeObject m_currentInstance = null;
@@ -54,17 +59,20 @@ public void SystemGC1_Test()
/// 6. Verify that object has been collected
///
///
- // Tests ReRegisterForFinalize
- // Create a FinalizeObject.
+
+ OutputHelper.WriteLine("Tests ReRegisterForFinalize");
+ OutputHelper.WriteLine("Create a FinalizeObject.");
+
FinalizeObject mfo = new FinalizeObject();
m_hasFinalized1 = false;
m_hasFinalized2 = false;
// Release reference
+ OutputHelper.WriteLine("Release reference");
mfo = null;
- // Allow GC
- GC.WaitForPendingFinalizers();
+ OutputHelper.WriteLine("Allow GC");
+ GC.Collect();
int sleepTime = 1000;
int slept = 0;
@@ -85,10 +93,10 @@ public void SystemGC1_Test()
// FinalizeObject.m_currentInstance field. Setting this value
// to null and forcing another garbage collection will now
// cause the object to Finalize permanently.
- // Reregister and allow for GC
- FinalizeObject.m_currentInstance = null;
- GC.WaitForPendingFinalizers();
+ OutputHelper.WriteLine("Reregister and allow for GC");
+ FinalizeObject.m_currentInstance = null;
+ GC.Collect();
sleepTime = 1000;
slept = 0;
@@ -119,18 +127,19 @@ public void SystemGC2_Test()
/// 6. Verify that object has not been collected
///
///
- // Tests SuppressFinalize
- // Create a FinalizeObject.
+
+ OutputHelper.WriteLine("Tests SuppressFinalize");
+ OutputHelper.WriteLine("Create a FinalizeObject");
FinalizeObject mfo = new FinalizeObject();
m_hasFinalized1 = false;
m_hasFinalized2 = false;
- // Releasing
+ OutputHelper.WriteLine("Releasing");
GC.SuppressFinalize(mfo);
mfo = null;
- // Allow GC
- GC.WaitForPendingFinalizers();
+ OutputHelper.WriteLine("Allow GC");
+ GC.Collect();
int sleepTime = 1000;
int slept = 0;
@@ -138,7 +147,7 @@ public void SystemGC2_Test()
while (!m_hasFinalized1 && slept < sleepTime)
{
// force GC run caused by memory allocation
- var dummyArray = new byte[1024 * 1024 * 1];
+ _ = new byte[1024 * 1024 * 1];
System.Threading.Thread.Sleep(10);
slept += 10;
@@ -161,59 +170,35 @@ public void SystemGC3_Test()
///
///
- // Tests WaitForPendingFinalizers, dependant on test 1
- // will auto-fail if test 1 fails.
OutputHelper.Write("Tests WaitForPendingFinalizers, dependant on test 1");
- OutputHelper.WriteLine("will auto-fail if test 1 fails.");
+ OutputHelper.WriteLine("will fail if test 1 fails.");
- Assert.IsTrue(m_Test1Result);
+ Assert.IsTrue(m_Test1Result, "Can't run this test as SystemGC1_Test has failed.");
- // Create a FinalizeObject.
+ OutputHelper.WriteLine("Create a FinalizeObject");
FinalizeObject mfo = new FinalizeObject();
m_hasFinalized1 = false;
m_hasFinalized2 = false;
- // Releasing
+ OutputHelper.WriteLine("Releasing");
mfo = null;
- int sleepTime = 1000;
- int slept = 0;
-
- while (!m_hasFinalized1 && slept < sleepTime)
- {
- // force GC run caused by memory allocation
- var dummyArray = new byte[1024 * 1024 * 1];
-
- System.Threading.Thread.Sleep(10);
- slept += 10;
- }
-
- OutputHelper.WriteLine($"GC took {slept}");
-
- // Wait for GC
+ OutputHelper.WriteLine("Wait for GC");
+ GC.Collect();
GC.WaitForPendingFinalizers();
- // Releasing again
+ OutputHelper.WriteLine("Releasing again");
FinalizeObject.m_currentInstance = null;
- sleepTime = 1000;
- slept = 0;
-
- while (!m_hasFinalized2 && slept < sleepTime)
- {
- // force GC run caused by memory allocation
- var dummyArray = new byte[1024 * 1024 * 1];
-
- System.Threading.Thread.Sleep(10);
- slept += 10;
- }
-
- OutputHelper.WriteLine($"GC took {slept}");
-
- // Wait for GC
+ OutputHelper.WriteLine("Wait for GC");
+ GC.Collect();
GC.WaitForPendingFinalizers();
Assert.IsTrue(m_hasFinalized2);
}
}
+#pragma warning restore S1215 // "GC.Collect" should not be called
+#pragma warning restore S1854 // Unused assignments should be removed
+#pragma warning restore S2696 // Instance members should not write to "static" fields
+#pragma warning restore S3971 // "GC.SuppressFinalize" should not be called
}
diff --git a/nanoFramework.CoreLibrary/System/GC.cs b/nanoFramework.CoreLibrary/System/GC.cs
index 13f07e8..7746f4f 100644
--- a/nanoFramework.CoreLibrary/System/GC.cs
+++ b/nanoFramework.CoreLibrary/System/GC.cs
@@ -1,39 +1,79 @@
-// Licensed to the .NET Foundation under one or more agreements.
+// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
+using System.Runtime.CompilerServices;
+
namespace System
{
- using Runtime.CompilerServices;
-
///
/// Controls the system garbage collector, a service that automatically reclaims unused memory.
///
public static class GC
{
+#pragma warning disable S4200 // Native methods should be wrapped
+
+ ///
+ /// Enables or disables the output of garbage collection messages.
+ ///
+ /// to enable the output of GC messages; otherwise, .
+ ///
+ ///
+ /// Enabling GC messages may not always result in output, depending on the target build options.
+ /// For example, RTM builds, which remove all non-essential features, may not output these messages.
+ ///
+ ///
+ /// This method is specific of .NET nanoFramework implementation. There is no equivalent in full .NET API.
+ ///
+ ///
[MethodImpl(MethodImplOptions.InternalCall)]
- private static extern bool AnyPendingFinalizers();
+ public static extern void EnableGCMessages(bool enable);
///
- /// Suspends the current thread until the thread that is processing the queue of finalizers has emptied that queue.
+ /// Retrieves the heap size excluding fragmentation. For example if the total GC heap size is 1MB and fragmentation, ie, space taken up by free objects, takes up 400kB, this API would report 600kB. A parameter indicates whether this method can wait a short interval before returning, to allow the system to collect garbage and finalize objects.
///
- public static void WaitForPendingFinalizers()
- {
- while (AnyPendingFinalizers()) Threading.Thread.Sleep(10);
- }
+ /// to indicate that this method can wait for garbage collection and heap compaction to occur before returning; otherwise, .
+ /// The heap size, in bytes, excluding fragmentation.
+ public static long GetTotalMemory(bool forceFullCollection) => Run(forceFullCollection);
///
- /// Requests that the system not call the finalizer for the specified object.
+ /// Requests that the system call the finalizer for the specified object for which has previously been called.
///
- /// The object that a finalizer must not be called for.
+ /// The object that a finalizer must be called for.
+ /// is .
[MethodImpl(MethodImplOptions.InternalCall)]
- public static extern void SuppressFinalize(Object obj);
+ public static extern void ReRegisterForFinalize(object obj);
+
+ ///
+ /// Forces an immediate garbage collection of all generations.
+ ///
+ ///
+ /// Use this method to try to reclaim all memory that is inaccessible. It performs a blocking garbage collection of all generations.
+ /// All objects, regardless of how long they have been in memory, are considered for collection; however, objects that are referenced in managed code are not collected. Use this method to force the system to try to reclaim the maximum amount of available memory.
+ ///
+ public static void Collect() => Run(true);
///
- /// Requests that the system call the finalizer for the specified object for which SuppressFinalize has previously been called.
+ /// Requests that the common language runtime not call the finalizer for the specified object.
///
- /// The object that a finalizer must be called for.
+ /// The object whose finalizer must not be executed.
+ /// is .
[MethodImpl(MethodImplOptions.InternalCall)]
- public static extern void ReRegisterForFinalize(Object obj);
+ public static extern void SuppressFinalize(object obj);
+ ///
+ /// Suspends the current thread until the thread that is processing the queue of finalizers has emptied that queue.
+ ///
+ public static void WaitForPendingFinalizers()
+ {
+ while (AnyPendingFinalizers()) Threading.Thread.Sleep(10);
+ }
+
+#pragma warning restore S4200 // Native methods should be wrapped
+
+ [MethodImpl(MethodImplOptions.InternalCall)]
+ private static extern bool AnyPendingFinalizers();
+
+ [MethodImpl(MethodImplOptions.InternalCall)]
+ private static extern uint Run(bool compactHeap);
}
}