| 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465 |
- namespace Jint.Tests.Runtime;
- // This needs to run without any parallelization because it uses
- // garbage collector metrics which cannot be isolated.
- [CollectionDefinition(nameof(GarbageCollectionTests), DisableParallelization = true)]
- [Collection(nameof(GarbageCollectionTests))]
- public class GarbageCollectionTests
- {
- [Fact]
- public void InternalCachingDoesNotPreventGarbageCollection()
- {
- // This test ensures that memory allocated within functions
- // can be garbage collected by the .NET runtime. To test that,
- // the "allocate" functions allocates a big chunk of memory,
- // which is not used anywhere. So the GC should have no problem
- // releasing that memory after the "allocate" function leaves.
- // Arrange
- var engine = new Engine();
- const string script =
- """
- function allocate(runAllocation) {
- if (runAllocation) {
- // Allocate ~200 MB of data (not 100 because .NET uses UTF-16 for strings)
- var test = Array.from({ length: 100 })
- .map(() => ' '.repeat(1 * 1024 * 1024));
- }
- return 2;
- }
- """;
- engine.Evaluate(script);
- // Create a baseline for memory usage.
- engine.Evaluate("allocate(false);");
- var usedMemoryBytesBaseline = CurrentlyUsedMemory();
- // Act
- engine.Evaluate("allocate(true);");
- // Assert
- var usedMemoryBytesAfterJsScript = CurrentlyUsedMemory();
- var epsilon = 10 * 1024 * 1024; // allowing up to 10 MB of other allocations should be enough to prevent false positives
- Assert.True(
- usedMemoryBytesAfterJsScript - usedMemoryBytesBaseline < epsilon,
- userMessage: $"""
- The garbage collector did not free the allocated but unreachable 200 MB from the script.;
- Before Call : {BytesToString(usedMemoryBytesBaseline)}
- After Call : {BytesToString(usedMemoryBytesAfterJsScript)}
- ---
- Acceptable : {BytesToString(usedMemoryBytesBaseline + epsilon)}
- """);
- return;
- static string BytesToString(long bytes)
- => $"{(bytes / 1024.0 / 1024.0),6:0.0} MB";
- static long CurrentlyUsedMemory()
- {
- // Just try to ensure that everything possible gets collected.
- GC.Collect(2, GCCollectionMode.Forced, blocking: true);
- var currentlyUsed = GC.GetTotalMemory(forceFullCollection: true);
- return currentlyUsed;
- }
- }
- }
|