北海哪家做網(wǎng)站百度推廣怎么做
我一直在思考為何Redis這種應用就能獨占那么大的內(nèi)存空間而我開發(fā)的應用為何只有4GB大小左右,在此基礎(chǔ)上也問了一些大佬,最終還是驗證下自己的猜測。
操作系統(tǒng)限制
主要為32位操作系統(tǒng)和64位操作系統(tǒng)。
每個進程自身還分為了用戶進程空間和內(nèi)核進程空間,基本上各一半,而應用本身主要的空間就是用戶進程空間。
32位操作系統(tǒng)尋址長度
尋址總線寬度32位,2^32次方,也就是4GB 大小
那么,用戶態(tài)空間(用戶空間 只有2G)
64位操作系統(tǒng)尋址長度
尋址總線實際總線寬度48位,2^48次方,也就是256TB 大小
操作系統(tǒng)本身的限制(Windows)
以上說的是32位進程的用戶模式虛擬地址空間為2GB,在32位系統(tǒng)上可以打開3GB開關(guān)或者采用4GT技術(shù)后,最多能達到3GB的用戶空間,在64位系統(tǒng)上默認是打開的,最多分配4GB的虛擬用戶模式內(nèi)存。
而在64位進程的用戶模擬虛擬空間,在32位上不適用,而在64位系統(tǒng)中默認開啟了這個功能,并且能達到8TB以上的虛擬內(nèi)存。
這個圖說的是實際上在這個操作系統(tǒng)下,真實的物理內(nèi)存 在86,也就是32位下最多只有4GB 物理內(nèi)存的支持,那為啥我們看到實際上win7 32位也支持很大的內(nèi)存條,那是因為開啟了 物理地址擴展 PAE 功能,而應用自身的尋址空間是不變的。
可以明顯感覺到 Win11 比 Win10 能支持的物理內(nèi)存更大
服務器版本的操作系統(tǒng)支持的更更大,當然,也沒有得到 物理系統(tǒng)本身的極限 256TB。
也說明了實際的物理內(nèi)存,服務器版本會支持更大的物理內(nèi)存。
.NET 應用自身的限制
.NET 這邊因為有CLR的存在,把內(nèi)存又分為了托管內(nèi)存和非托管內(nèi)存,而用戶態(tài)空間,也就是用戶空間實際上就是托管內(nèi)存空間,它的大小實際上是限制住的。
所以實際上,托管數(shù)組的長度限制在0x7FFFFFC7了,官方的說法是為了防止溢出(《.NET 運行時 最大長度限制》)。
Retrieved 280000000 items limit:2147483591 out:False 0GB個 of data .2 GB
大概意思就是,創(chuàng)建了 280000000的隨機數(shù),double類型的,數(shù)組的極限是0x7FFFFFC7( 2147483591),是否超出了這個極限,大概有多少GB條數(shù)據(jù),一共占用多少GB空間。
可以看到最后一條數(shù)據(jù)
一共創(chuàng)建了 2140000000條,距離極限相差 7,483,591條,基本證明,這個限制是存在的。
實際上,它一共占用了14GB 內(nèi)存(大概,實際上波動還挺大)
public static void Test0()
{Double[] values = GetData();// Compute mean.Console.WriteLine("Sample mean: {0}, N = {1}",GetMean(values), values.Length);static Double[] GetData(){var d = 0x7FFFFFC7;Random rnd = new Random();List<Double> values = new List<Double>();for (int ctr = 1; ctr <= int.MaxValue; ctr++){values.Add(rnd.NextDouble());if (ctr % 10000000 == 0){var memSize = ((long)values.Count * 8) / 1024 / 1024 / 1024;Console.WriteLine($"Retrieved {ctr} items limit:{d} out:{ctr >= d} {(long)values.Count / 1024 / 1024 / 1024}GB個 of data .{memSize} GB");}}return values.ToArray();}static Double GetMean(Double[] values){Double sum = 0;foreach (var value in values)sum += value;return sum / values.Length;}
}
這是64位應用自身可以操作大內(nèi)存的驗證。
而32位應用只操作了6千萬條數(shù)據(jù)就內(nèi)存溢出了,如下圖。
非托管內(nèi)存申請大內(nèi)存
public static void Test2()
{var list = new List<IntPtr>();try{for (int i = 0; i < 8; i++){var ptr = Marshal.AllocHGlobal(int.MaxValue);//默認最大2G申請,單個方法list.Add(ptr);for (int j = 0; j < int.MaxValue; j++){Marshal.WriteByte(ptr, j, (byte)(66 + i));}Console.WriteLine($"寫入成功{i}");}Console.WriteLine("申請完成");Console.ReadLine();}catch (Exception ex){Console.WriteLine(ex.Message);}finally{foreach (var item in list){Marshal.FreeHGlobal(item);}}
}
64位應用
寫入全部成功
內(nèi)存占用也基本占滿了整個內(nèi)存,剩余的16GB。
32位應用
而32位應用程序,直接內(nèi)存就溢出了。
所以也證明,非托管資源跟32位進程尋址空間是有關(guān)系的。
大內(nèi)存應用的方案
大內(nèi)存應該是大于4G內(nèi)存的才叫大內(nèi)存。
基本上就不太考慮32位應用了。畢竟32位應用的尋址空間太過受限,盡量采用64位應用開發(fā),可以使用托管資源實現(xiàn)大內(nèi)存應用和非托管內(nèi)存實現(xiàn)大應用。
MemoryMappedFiles (內(nèi)存文件映射方案)
這個方案的好處是,雖然應用空間最小2GB,但是,可以在這2GB空間里實現(xiàn)視窗尋址文件,實現(xiàn)另外一種大內(nèi)存的方案。
也不受限于應用的地址位數(shù)(86,64)。
Marshal.AllocHGlobal (非托管資管)
用這個的話,感覺回到了C語言時代,需要自己管理資源的申請與釋放,另外,只有64位系統(tǒng)才會有更多的內(nèi)存申請。
64位應用
在托管資源下,64位應用本身的空間已經(jīng)能占用很大的空間,足夠進行大內(nèi)存應用的開發(fā)。也建議使用這種方式。
多進程
另外一種簡單的方案就是采用多進程的方式實現(xiàn)多占內(nèi)存資源。
代碼地址
https://github.com/kesshei/MemeryTest.git
https://gitee.com/kesshei/MemeryTest.git
總結(jié)
一直在思考大內(nèi)存的應用,如何申請大的內(nèi)存,只有實際測試和驗證才知道有哪種以及哪種的方式是最佳的。
現(xiàn)在才明白,Redis 64位系統(tǒng)不限制內(nèi)存,32位系統(tǒng)最多使用3GB內(nèi)存。所以,如果你想開發(fā)一個類Redis這種的中間件,內(nèi)存的限制就這么多。
參考資料地址
《Windows 和 Windows Server 版本的內(nèi)存限制》
https://learn.microsoft.com/zh-cn/windows/win32/memory/memory-limits-for-windows-releases?redirectedfrom=MSDN
《What Is 4GT? 什么是4GT?》
https://learn.microsoft.com/zh-cn/previous-versions/windows/it-pro/windows-server-2003/cc786709(v=ws.10)
《物理地址擴展 PAE》
https://learn.microsoft.com/zh-cn/windows/win32/memory/physical-address-extension
《.NET 運行時 最大長度限制》
https://github.com/dotnet/runtime/blob/f107b63fca1bd617a106e3cc7e86b337151bff79/src/coreclr/vm/gchelpers.cpp#L350
閱
一鍵三連呦!,感謝大佬的支持,您的支持就是我的動力!