// // Copyright (c) Microsoft Corporation. All rights reserved. // /*******************************************************************/ /* WARNING */ /* This file should be identical in the Bartok and Singularity */ /* depots. Master copy resides in Bartok Depot. Changes should be */ /* made to Bartok Depot and propagated to Singularity Depot. */ /*******************************************************************/ namespace System.GCs { using System.Runtime.CompilerServices; using System.Runtime.InteropServices; #if SINGULARITY_KERNEL using Microsoft.Singularity; using Microsoft.Singularity.Memory; using Sing_MemoryManager = Microsoft.Singularity.Memory.MemoryManager; #elif SINGULARITY_PROCESS using Microsoft.Singularity; using Microsoft.Singularity.V1.Services; #endif internal class MemoryManager { private static UIntPtr allocationGranularity; private static UIntPtr totalMemorySize; private static UIntPtr operatingSystemSize; private static bool inAllocator; #if SINGULARITY internal static unsafe void Initialize() { allocationGranularity = (UIntPtr)0x10000; totalMemorySize = PageTable.PageSize * PageTable.pageTableCount; operatingSystemSize = totalMemorySize / 16; } //////////////////////////////////// Allocation and Free Routines. // // Allocation is optimized for the case where an allocation starts // with a relatively small amount of memory and grows over time. // This is exactly the behavior exhibited by stacks and GC heaps. // // The allocation strategy also works well for large initial // allocations. The strategy would be very inefficient if a very // large number of small, completely independent allocations are // made. // // AllocateMemory(size) performs an initial allocation. // AllocateMemory(startAddr, size) performs growing allocations. // internal static unsafe UIntPtr AllocateMemory(UIntPtr size) { VTable.Assert(PageTable.PageAligned(size)); #if SINGULARITY_KERNEL UIntPtr addr = Sing_MemoryManager.KernelAllocate( Sing_MemoryManager.PagesFromBytes(size), Process.kernelProcess, 0, PageType.Unknown); #elif SINGULARITY_PROCESS UIntPtr addr = PageTableService.Allocate(size); #endif #if SINGULARITY_KERNEL Kernel.Waypoint((int)size); Kernel.Waypoint(811); #endif // SINGULARITY_KERNEL if (addr != UIntPtr.Zero) { Util.MemClear(addr, size); } return addr; } internal static unsafe bool AllocateMemory(UIntPtr startAddr, UIntPtr size) { VTable.Deny(inAllocator); inAllocator = true; VTable.Assert(PageTable.PageAligned(startAddr)); VTable.Assert(PageTable.PageAligned(size)); #if SINGULARITY_KERNEL UIntPtr addr = Sing_MemoryManager.KernelExtend( startAddr, Sing_MemoryManager.PagesFromBytes(size), Process.kernelProcess, PageType.Unknown); #elif SINGULARITY_PROCESS UIntPtr addr = PageTableService.AllocateExtend(startAddr, size); #endif inAllocator = false; if (addr != UIntPtr.Zero) { Util.MemClear(addr, size); return true; } return false; } [Inline] internal static void FreeMemory(UIntPtr startAddr, UIntPtr size) { #if SINGULARITY_KERNEL DebugStub.Assert(Sing_MemoryManager.IsPageAligned(size)); Sing_MemoryManager.KernelFree( startAddr, Sing_MemoryManager.PagesFromBytes(size), Process.kernelProcess); #elif SINGULARITY_PROCESS VTable.Assert((size & PageTable.PageMask) == 0); PageTableService.Free(startAddr, size); #endif } internal static void IgnoreMemoryContents(UIntPtr startAddr, UIntPtr regionSize) { // Since we don't do swapping, we simply ignore this information } internal static bool QueryMemory(UIntPtr queryAddr, out UIntPtr regionAddr, out UIntPtr regionSize) { #if SINGULARITY_KERNEL PageType type = Sing_MemoryManager.KernelQuery( queryAddr, out regionAddr, out regionSize); return (type != PageType.Unknown); #elif SINGULARITY_PROCESS return PageTableService.Query(queryAddr, out regionAddr, out regionSize); #endif } #else // not SINGULARITY [NoBarriers] [PreInitRefCounts] [NoStackLinkCheckTrans] internal static unsafe void Initialize() { SYSTEM_INFO systemInfo; GetSystemInfo(out systemInfo); allocationGranularity = new UIntPtr(systemInfo.AllocationGranularity); MEMORYSTATUSEX memoryStatus = new MEMORYSTATUSEX(); memoryStatus.Length = (uint) sizeof(MEMORYSTATUSEX); GlobalMemoryStatusEx(ref memoryStatus); if (memoryStatus.TotalPhysical <= ((ulong)UIntPtr.MaxValue)) { totalMemorySize = (UIntPtr) memoryStatus.TotalPhysical; } else { // TotalPhysical exceeds UIntPtr.MaxValue running in 32bit // mode on a machine with more than 4GB of RAM totalMemorySize = UIntPtr.MaxValue; } // We are just making a random guess here operatingSystemSize = (UIntPtr) 1 << 26; } // Low-level routines based on Win32 internal const int PAGE_READWRITE = 0x04; internal const int PAGE_EXECUTE_READWRITE = 0x40; internal const int PAGE_NOACCESS = 0x01; internal const int MEM_COMMIT = 0x1000; internal const int MEM_RESERVE = 0x2000; private const int MEM_DECOMMIT = 0x4000; internal const int MEM_RELEASE = 0x8000; private const int MEM_FREE = 0x10000; private const int MEM_RESET = 0x80000; [StructLayout(LayoutKind.Sequential)] private struct MEMORY_BASIC_INFORMATION { internal UIntPtr BaseAddress; internal UIntPtr AllocationBase; internal int AllocationProtect; internal UIntPtr RegionSize; internal int State; internal int Protect; internal int Type; } [StructLayout(LayoutKind.Sequential)] private struct SYSTEM_INFO { internal ushort ProcessorArchitecture; internal ushort Reserved; internal uint PageSize; internal UIntPtr MinimumApplicationAddress; internal UIntPtr MaximumApplicationAddress; internal uint ActiveProcessorMask; internal uint NumberOfProcessors; internal uint ProcessorType; internal uint AllocationGranularity; internal ushort ProcessorLevel; internal ushort ProcessorRevision; } [StructLayout(LayoutKind.Sequential)] private struct MEMORYSTATUSEX { internal uint Length; internal uint MemoryLoad; internal ulong TotalPhysical; internal ulong AvailPhysical; internal ulong TotalPageFile; internal ulong AvailPageFile; internal ulong TotalVirtual; internal ulong AvailVirtual; internal ulong AvailExtendedVirtual; } // Kernel32.dll functions (via MSVCRT.dll) [DllImport("BRT")] [GCAnnotation(GCOption.NOGC)] [NoStackLinkCheck] [StackBound(1024)] public static unsafe extern void *VirtualAlloc(void *lpAddress, UIntPtr dwSize, int fIAllocationType, int fIProtect); [DllImport("BRT")] [GCAnnotation(GCOption.NOGC)] [StackBound(1024)] internal static unsafe extern int VirtualFree(void *lpAddress, UIntPtr dwSize, int dwFreeType); [DllImport("BRT")] [GCAnnotation(GCOption.NOGC)] [StackBound(1024)] private static unsafe extern UIntPtr VirtualQuery(void *lpAddress, out MEMORY_BASIC_INFORMATION memInfo, UIntPtr dwLength); [DllImport("BRT")] [GCAnnotation(GCOption.NOGC)] [NoStackLinkCheck] [StackBound(1024)] [NoBarriers] private static unsafe extern void GetSystemInfo(out SYSTEM_INFO systemInfo); [DllImport("BRT")] [GCAnnotation(GCOption.NOGC)] [NoStackLinkCheck] [StackBound(1024)] [NoBarriers] private static unsafe extern void GlobalMemoryStatusEx(ref MEMORYSTATUSEX memoryStatus); // Low-level routines based on the operating system interface private static unsafe UIntPtr AllocateMemoryHelper(UIntPtr startAddr, UIntPtr size) { void *result = VirtualAlloc((void *) startAddr, size, MEM_RESERVE, PAGE_READWRITE); VTable.Assert (PageTable.Page((UIntPtr) result + size - 1) < PageTable.pageTableCount, "OutOfMemory: MemoryManager: memory doesn't fit in page table"); if (result != null) { Trace.Log(Trace.Area.Page, "VirtualAlloc {0} at {1}", __arglist(size, result)); VTable.Assert((startAddr == UIntPtr.Zero) || (result == (void *) startAddr)); void *area = VirtualAlloc(result, size, MEM_COMMIT, PAGE_READWRITE); if (PageTable.halPageDescriptor != null) { PageTable.CreateNewPageTablesIfNecessary(PageTable.Page((UIntPtr)result), PageTable.PageCount(size)); PageTable.SetProcess(PageTable.Page((UIntPtr) area), PageTable.PageCount(size)); } VTable.Assert(result == area); #if HIMEM // This assertion intends only to catch bugs in Bartok. But if // the system (windows) frees memory before, the memory // may be in low memory, and if we happen to get that // memory, this assertion itself may not hold VTable.Assert((UIntPtr) result >= PageTable.HIMEMStart, ("High memory is expected to be allocated in HIMEM mode")); #endif } return new UIntPtr(result); } internal static unsafe bool AllocateMemory(UIntPtr startAddr, UIntPtr size) { UIntPtr addr = AllocateMemoryHelper(startAddr, size); return addr != UIntPtr.Zero; } internal static unsafe UIntPtr AllocateMemory(UIntPtr size) { return AllocateMemoryHelper(UIntPtr.Zero, size); } internal static unsafe void FreeMemory(UIntPtr addr, UIntPtr size) { Trace.Log(Trace.Area.Page, "VirtualFree {0} at {1}", __arglist(size, addr)); int success = VirtualFree((void *) addr, UIntPtr.Zero, MEM_RELEASE); VTable.Assert(success != 0); } // Indicate that we still own the memory but that we don't care // about the content of the memory. The virtual memory system // does not have to write the memory to the swap file in order // to reuse the physical memory for other purposes. internal static unsafe void IgnoreMemoryContents(UIntPtr startAddr, UIntPtr regionSize) { Trace.Log(Trace.Area.Page, "VirtualAlloc-Reset {0} at {1}", __arglist(regionSize, startAddr)); void * result = VirtualAlloc((void *) startAddr, regionSize, MEM_RESET, PAGE_READWRITE); VTable.Assert(result == (void *) startAddr, "VirtualAlloc MEM_RESET failed"); } internal static unsafe bool QueryMemory(UIntPtr queryAddr, out UIntPtr regionAddr, out UIntPtr regionSize) { VTable.Assert(PageTable.PageAligned(queryAddr)); MEMORY_BASIC_INFORMATION memInfo; UIntPtr size = (UIntPtr) sizeof(MEMORY_BASIC_INFORMATION); UIntPtr data = VirtualQuery((void *) queryAddr, out memInfo, size); Trace.Log(Trace.Area.Page, "VirtualQuery {0}: base={1} size={2}", __arglist(queryAddr, memInfo.AllocationBase, memInfo.RegionSize)); if (data == 0) { // queryAddr is a kernel-mode pointer regionAddr = queryAddr; regionSize = (UIntPtr) sizeof(int); return false; } else { VTable.Assert(data == size && memInfo.BaseAddress == queryAddr); regionAddr = memInfo.AllocationBase; regionSize = (queryAddr - regionAddr) + memInfo.RegionSize; return (memInfo.State != MEM_FREE); } } #endif internal static UIntPtr OperatingSystemCommitSize { get { return allocationGranularity; } } // We are just guessing here internal static UIntPtr MemorySize { get { return totalMemorySize; } } // We are just making an educated guess here internal static UIntPtr OperatingSystemSize { get { return operatingSystemSize; } } } }