如何让.NET应用使用更大的内存

我一直在思考为何Redis这种应用就能独占那么大的内存空间而我开发的应用为何只有4GB大小左右,在此基础上也问了一些大佬,最终还是验证下自己的猜测。

操作系统限制

主要为32位操作系统和64位操作系统。

每个进程自身还分为了用户进程空间和内核进程空间,基本上各一半,而应用本身主要的空间就是用户进程空间。

32位操作系统寻址长度

寻址总线宽度32位,2^32次方,也就是4GB 大小

那么,用户态空间(用户空间 只有2G)

64位操作系统寻址长度

寻址总线实际总线宽度48位,2^48次方,也就是256TB 大小

操作系统本身的限制(Windows)

以上说的是32位进程的用户模式虚拟地址空间为2GB,在32位系统上可以打开3GB开关或者采用4GT技术后,最多能达到3GB的用户空间,在64位系统上默认是打开的,最多分配4GB的虚拟用户模式内存。

而在64位进程的用户模拟虚拟空间,在32位上不适用,而在64位系统中默认开启了这个功能,并且能达到8TB以上的虚拟内存。

这个图说的是实际上在这个操作系统下,真实的物理内存 在86,也就是32位下最多只有4GB 物理内存的支持,那为啥我们看到实际上win7 32位也支持很大的内存条,那是因为开启了 物理地址扩展 PAE 功能,而应用自身的寻址空间是不变的。


可以明显感觉到 Win11 比 Win10 能支持的物理内存更大

服务器版本的操作系统支持的更更大,当然,也没有得到 物理系统本身的极限 256TB。

也说明了实际的物理内存,服务器版本会支持更大的物理内存。

.NET 应用自身的限制

.NET 这边因为有CLR的存在,把内存又分为了托管内存和非托管内存,而用户态空间,也就是用户空间实际上就是托管内存空间,它的大小实际上是限制住的。

所以实际上,托管数组的长度限制在0x7FFFFFC7了,官方的说法是为了防止溢出(《.NET 运行时 最大长度限制》)。

Retrieved 280000000 items limit:2147483591 out:False 0GB个 of data .2 GB

大概意思就是,创建了 280000000的随机数,double类型的,数组的极限是0x7FFFFFC7( 2147483591),是否超出了这个极限,大概有多少GB条数据,一共占用多少GB空间。

可以看到最后一条数据
一共创建了 2140000000条,距离极限相差 7,483,591条,基本证明,这个限制是存在的。
实际上,它一共占用了14GB 内存(大概,实际上波动还挺大)

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位应用自身可以操作大内存的验证。

而32位应用只操作了6千万条数据就内存溢出了,如下图。

非托管内存申请大内存

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位应用

写入全部成功

内存占用也基本占满了整个内存,剩余的16GB。

32位应用

而32位应用程序,直接内存就溢出了。

所以也证明,非托管资源跟32位进程寻址空间是有关系的。

大内存应用的方案

大内存应该是大于4G内存的才叫大内存。

基本上就不太考虑32位应用了。毕竟32位应用的寻址空间太过受限,尽量采用64位应用开发,可以使用托管资源实现大内存应用和非托管内存实现大应用。

MemoryMappedFiles (内存文件映射方案)

这个方案的好处是,虽然应用空间最小2GB,但是,可以在这2GB空间里实现视窗寻址文件,实现另外一种大内存的方案。
也不受限于应用的地址位数(86,64)。

Marshal.AllocHGlobal (非托管资管)

用这个的话,感觉回到了C语言时代,需要自己管理资源的申请与释放,另外,只有64位系统才会有更多的内存申请。

64位应用

在托管资源下,64位应用本身的空间已经能占用很大的空间,足够进行大内存应用的开发。也建议使用这种方式。

多进程

另外一种简单的方案就是采用多进程的方式实现多占内存资源。

代码地址

https://github.com/kesshei/MemeryTest.git

https://gitee.com/kesshei/MemeryTest.git

总结

一直在思考大内存的应用,如何申请大的内存,只有实际测试和验证才知道有哪种以及哪种的方式是最佳的。
现在才明白,Redis 64位系统不限制内存,32位系统最多使用3GB内存。所以,如果你想开发一个类Redis这种的中间件,内存的限制就这么多。

参考资料地址

《Windows 和 Windows Server 版本的内存限制》
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

一键三连呦!,感谢大佬的支持,您的支持就是我的动力!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/252085.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

数码管的动态显示

说到动态显示&#xff0c;我们可以说是轻车熟路了&#xff0c;之前的LED已经练过不少了&#xff0c;此次只是把LED换成了数码管&#xff0c;原理一样&#xff0c;还是一样的电路&#xff0c;接下来看看如何做到动态显示。 首先是对程序代码做些更改&#xff0c;只要要加上扫描的…

maui中实现加载更多 RefreshView跟ListView 跳转到详情页 传参(3)

效果如图 这里的很多数据是通过传参过来的的。 代码 例表页加入跳转功能&#xff1a; <ListView ItemsSource"{Binding Items}" ItemAppearing"OnItemAppearing" ItemTapped"OnItemTapped" RowHeight"70" Margin"20"…

04 python函数

4.1 函数的快速开发体验 """ 演示&#xff0c;快速体验函数的开发和使用 """#需求&#xff0c;统计字符串的长度&#xff0c;不使用内置函数len()str1 itheima str2 itcast str3 python#定义一个计数的变量 count 0 for i in str1:count 1…

广西岑溪市火灾通报:1人死亡 AI科技助力预防悲剧

近日&#xff0c;广西岑溪市玉梧大道紫坭工业园一厂房发生一起令人心痛的火灾事件&#xff0c;造成1人不幸丧生。这起悲剧再次提醒我们&#xff0c;火灾的防范工作是多么的重要。在这样的背景下&#xff0c;我想分享一个能够有效预防类似悲剧的技术——北京富维图像公司开发的F…

C++ Qt开发:标准Dialog对话框组件

Qt 是一个跨平台C图形界面开发库&#xff0c;利用Qt可以快速开发跨平台窗体应用程序&#xff0c;在Qt中我们可以通过拖拽的方式将不同组件放到指定的位置&#xff0c;实现图形化开发极大的方便了开发效率&#xff0c;本章将重点介绍标准对话框QInputDialog、QFileDialog 这两种…

Java项目-瑞吉外卖Day5

视线新增套餐功能&#xff1a; 创建SetmealDish&#xff0c;SetmealDto类&#xff0c;与相关的mapper&#xff0c;service&#xff0c;serviceImpl&#xff0c;controller类。 Setmeal表示套餐&#xff0c;SetmealDish表示套餐对应的菜品。 交互过程&#xff1a; 前端请求&a…

XXE实体注入漏洞知识点

什么是XXE漏洞&#xff1f; XXE&#xff0c;即XML外部实体注入漏洞&#xff0c;XXE 漏洞发生在应用程序解析 XML 输入时&#xff0c; 没有禁止外部实体的加载 &#xff0c;导致可加载恶意外部文件&#xff0c;造成文件读取、命令执行、攻击内网网站等危险。 XXE漏洞触发的点…

网络(九)三层路由、DHCP以及VRRP协议介绍

目录 一、三层路由 1. 定义 2. 交换原理 3. 操作演示 3.1 图示 3.2 LSW1新建vlan10、20、30&#xff0c;分别对应123接口均为access类型&#xff0c;接口4为trunkl类型&#xff0c;允许所有vlan通过 3.3 LSW2新建vlan10、20、30&#xff0c;配置接口1为trunk类型&…

索尼(ILCE-7M3)MP4文件只能播放前两分钟修复案例

索尼的ILCE-7M3是一款经典设备&#xff0c;其HEVC编码效果是比较不错的&#xff0c;因此受到很多专业人士的青睐。之前我们说过很多索尼摄像机断电生成RSV文件修复的案例&#xff0c;今天来讲一个特殊的&#xff0c;文件已经正常封装但仅能播放前两分钟多一点的画面。 故障文件…

《Linux C编程实战》笔记:文件属性操作函数

获取文件属性 stat函数 在shell下直接使用ls就可以获得文件属性&#xff0c;但是在程序里应该怎么获得呢&#xff1f; #include<sys/types.h> #include <sys/stat.h> #include <unistd.h> int stat(const char *file_name,struct stat *buf); int fstat(i…

spring 笔记四 SpringMVC 组件解析

文章目录 SpringMVC 组件解析SpringMVC的执行流程SpringMVC的执行流程SpringMVC组件解析SpringMVC注解解析 SpringMVC 组件解析 SpringMVC的执行流程 SpringMVC的执行流程 ① 用户发送请求至前端控制器DispatcherServlet。 ② DispatcherServlet收到请求调用HandlerMapping处…

图像识别完整项目之Swin-Transformer,从获取关键词数据集到训练的完整过程

0. 前言 图像分类的大部分经典神经网络已经全部介绍完&#xff0c;并且已经作了测试 代码已经全部上传到资源&#xff0c;根据文章名或者关键词搜索即可 LeNet &#xff1a;pytorch 搭建 LeNet 网络对 CIFAR-10 图片分类 AlexNet &#xff1a; pytorch 搭建AlexNet 对花进行分…

SpringBoot上传图片文件到七牛云

准备工作 maven pom.xml添加七牛云的sdk依赖 <dependency><groupId>com.qiniu</groupId><artifactId>qiniu-java-sdk</artifactId><version>7.2.27</version></dependency>配置项 七牛云上传必要的配置有&#xff1a;acces…

四舍五入浮点数

1.题目如下&#xff1a; 2.方法一&#xff1a; 直接取出小数部分第一位来判断。 1. 先乘以10。 2. 强制类型转换为整型&#xff0c;去掉小数部分。 3. 再模10&#xff0c;相当于取出原数的小数第一位。 代码实现&#xff1a; int way1(double n) {int a (int)(n * 10);int b…

C++初学者使用Dev-C++5.11必备的小技巧

一、安装的软件是英文怎么办?陈老师来帮你解决! 步骤1:打开软件,不用我交了吧,看见一个单词长的像 Tools,看见了吧 步骤2:对,找到那个红色框子框起来的单词,最长的那个 步骤3:对,继续选择红色框子里的简体中文/Chinese,不是下面那个,注意,不要选错哟 步骤4:点击…

【️Java和C++主要的区别有哪些?各有哪些优缺点?】

✅Java和C主要的区别有哪些&#xff1f;各有哪些优缺点&#xff1f; ✅Java和C分别代表两种类型的语言✅ C是编译型语言✅ Java是解释型语言✅ 两者更多的主要区别如下&#xff1a; ✅知识拓展✅Java与C的参数方法有什么区别&#xff1f; ✅Java和C分别代表两种类型的语言 Java…

LeetCode day24

LeetCode day24 今天主打一个快乐happy(▽ʃ♡ƪ)&#xff0c;主要是今天写哈夫曼树被经典文件读取坑麻了&#xff08;为啥绝对路径能读取&#xff0c;相对不行。罢了&#xff09; 一个中等题&#xff0c;但是咋感觉很小学捏。。。 2177. 找到和为给定整数的三个连续整数 相…

消息队列有哪些应用场景?

分布式系统不同模块之间的通信&#xff0c;除了远程服务调用以外&#xff0c;消息中间件是另外一个重要的手段&#xff0c;在各种互联网系统设计中&#xff0c;消息队列有着广泛的应用。从本文开始&#xff0c;专栏进入分布式消息的模块&#xff0c;将讨论消息队列使用中的高频…

C++入门篇

呀哈喽&#xff0c;我是结衣。 了解完C的发展历程&#xff0c;我们当然也要会用C啊。今天这篇博客就是来帮助我们来入门C的&#xff0c;当然要入门C当然也要先学会C语言啦。在我学习C的过程中我会一直把C博客更新下去的。 C关键字 我们都知道C语言是有32个关键字的&#xff0…

Linux之进程(四)(进程地址空间)

目录 一、程序地址空间 二、进程地址空间 1、概念 2、写时拷贝 3、为什么要有进程地址空间 四、总结 一、程序地址空间 我们先来看看下面这张图。这张图是我们在学习语言时就见到过的内存区域划分图。 下面我们在Linux下看一看内存区域是不是也是这么划分的。 可见在Li…