1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
|
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System.IO;
using System.Runtime.InteropServices;
namespace System.Runtime
{
public sealed partial class MemoryFailPoint
{
private static ulong GetTopOfMemory()
{
Interop.Kernel32.SYSTEM_INFO info = new Interop.Kernel32.SYSTEM_INFO();
Interop.Kernel32.GetSystemInfo(out info);
return (ulong)info.lpMaximumApplicationAddress;
}
private static bool CheckForAvailableMemory(out ulong availPageFile, out ulong totalAddressSpaceFree)
{
bool r;
Interop.Kernel32.MEMORYSTATUSEX memory = new Interop.Kernel32.MEMORYSTATUSEX();
r = Interop.Kernel32.GlobalMemoryStatusEx(ref memory);
if (!r)
throw Win32Marshal.GetExceptionForLastWin32Error();
availPageFile = memory.availPageFile;
totalAddressSpaceFree = memory.availVirtual;
// Console.WriteLine($"Memory gate: Mem load: {memory.memoryLoad}% Available memory (physical + page file): {(memory.availPageFile >> 20)} MB Total free address space: {memory.availVirtual >> 20} MB GC Heap: {(GC.GetTotalMemory(true) >> 20)} MB");
return true;
}
// Based on the shouldThrow parameter, this will throw an exception, or
// returns whether there is enough space. In all cases, we update
// our last known free address space, hopefully avoiding needing to
// probe again.
private static unsafe bool CheckForFreeAddressSpace(ulong size, bool shouldThrow)
{
// Start walking the address space at 0. VirtualAlloc may wrap
// around the address space. We don't need to find the exact
// pages that VirtualAlloc would return - we just need to
// know whether VirtualAlloc could succeed.
ulong freeSpaceAfterGCHeap = MemFreeAfterAddress(null, size);
// Console.WriteLine($"MemoryFailPoint: Checked for free VA space. Found enough? {(freeSpaceAfterGCHeap >= size)} Asked for: {size} Found: {freeSpaceAfterGCHeap}");
// We may set these without taking a lock - I don't believe
// this will hurt, as long as we never increment this number in
// the Dispose method. If we do an extra bit of checking every
// once in a while, but we avoid taking a lock, we may win.
LastKnownFreeAddressSpace = (long)freeSpaceAfterGCHeap;
LastTimeCheckingAddressSpace = Environment.TickCount;
if (freeSpaceAfterGCHeap < size && shouldThrow)
throw new InsufficientMemoryException(SR.InsufficientMemory_MemFailPoint_VAFrag);
return freeSpaceAfterGCHeap >= size;
}
// Returns the amount of consecutive free memory available in a block
// of pages. If we didn't have enough address space, we still return
// a positive value < size, to help potentially avoid the overhead of
// this check if we use a MemoryFailPoint with a smaller size next.
private static unsafe ulong MemFreeAfterAddress(void* address, ulong size)
{
if (size >= s_topOfMemory)
return 0;
ulong largestFreeRegion = 0;
Interop.Kernel32.MEMORY_BASIC_INFORMATION memInfo = new Interop.Kernel32.MEMORY_BASIC_INFORMATION();
UIntPtr sizeOfMemInfo = (UIntPtr)sizeof(Interop.Kernel32.MEMORY_BASIC_INFORMATION);
while (((ulong)address) + size < s_topOfMemory)
{
UIntPtr r = Interop.Kernel32.VirtualQuery(address, ref memInfo, sizeOfMemInfo);
if (r == UIntPtr.Zero)
throw Win32Marshal.GetExceptionForLastWin32Error();
ulong regionSize = memInfo.RegionSize.ToUInt64();
if (memInfo.State == Interop.Kernel32.MEM_FREE)
{
if (regionSize >= size)
return regionSize;
else
largestFreeRegion = Math.Max(largestFreeRegion, regionSize);
}
address = (void*)((ulong)address + regionSize);
}
return largestFreeRegion;
}
// Allocate a specified number of bytes, commit them and free them. This should enlarge
// page file if necessary and possible.
private static void GrowPageFileIfNecessaryAndPossible(UIntPtr numBytes)
{
unsafe
{
#if ENABLE_WINRT
void* pMemory = Interop.mincore.VirtualAllocFromApp(null, numBytes, Interop.Kernel32.MEM_COMMIT, Interop.Kernel32.PAGE_READWRITE);
#else
void* pMemory = Interop.Kernel32.VirtualAlloc(null, numBytes, Interop.Kernel32.MEM_COMMIT, Interop.Kernel32.PAGE_READWRITE);
#endif
if (pMemory != null)
{
bool r = Interop.Kernel32.VirtualFree(pMemory, UIntPtr.Zero, Interop.Kernel32.MEM_RELEASE);
if (!r)
throw Win32Marshal.GetExceptionForLastWin32Error();
}
}
}
}
}
|