C# MemoryMappedFile.CreateViewStream()的托管内存使用

C# MemoryMappedFile.CreateViewStream()的托管内存使用,c#,memory-mapped-files,C#,Memory Mapped Files,MemoryMappedFile.CreateViewStream(0,len)会分配一个大小为len的托管内存块,还是会分配较小的缓冲区作为非托管数据上的滑动窗口 我想知道,因为我的目标是替换一个用于反序列化的中间缓冲区,它现在是一个内存流,这给大型数据集带来了麻烦,既因为缓冲区的大小,也因为LOH碎片 如果viewstream的内部缓冲区变为相同大小,那么进行此切换就没有意义 编辑: 在一个快速测试中,我在比较MemoryStream和MemoryMapped文件时发现了这些数字。来自GC.

MemoryMappedFile.CreateViewStream(0,len)
会分配一个大小为
len
的托管内存块,还是会分配较小的缓冲区作为非托管数据上的滑动窗口

我想知道,因为我的目标是替换一个用于反序列化的中间缓冲区,它现在是一个内存流,这给大型数据集带来了麻烦,既因为缓冲区的大小,也因为LOH碎片

如果viewstream的内部缓冲区变为相同大小,那么进行此切换就没有意义

编辑:

在一个快速测试中,我在比较MemoryStream和MemoryMapped文件时发现了这些数字。来自
GC.GetTotalMemory(true)/1024和
Process.GetCurrentProcess.VirtualMemorySize64/1024的读数

分配1GB内存流:

托管虚拟机
首字母:81 kB 190 896 kB
分配后:1024084KB 1244852 kB
正如预期的那样,一个gig的托管和虚拟内存。 现在,对于MemoryMappedFile:

托管虚拟机
首字母:81 kB 189 616 kB
已分配内存:84 kB 189 684 kB
1GB视流分配:84 kB 1213 368 kB
Viewstream已释放:84 kB 190 964 kB
因此,使用一个不太科学的测试,我的假设是ViewStream只使用非托管数据。正确吗?

在我看来,给出一个例子,你会得到一个滑动窗口,至少这是我在阅读这个例子时的解释

为方便起见,以下是示例:

    using System;
using System.IO;
using System.IO.MemoryMappedFiles;
using System.Runtime.InteropServices;

class Program
{
    static void Main(string[] args)
    {
        long offset = 0x10000000; // 256 megabytes 
        long length = 0x20000000; // 512 megabytes 

        // Create the memory-mapped file. 
        using (var mmf = MemoryMappedFile.CreateFromFile(@"c:\ExtremelyLargeImage.data", FileMode.Open,"ImgA"))
        {
            // Create a random access view, from the 256th megabyte (the offset) 
            // to the 768th megabyte (the offset plus length). 
            using (var accessor = mmf.CreateViewAccessor(offset, length))
            {
                int colorSize = Marshal.SizeOf(typeof(MyColor));
                MyColor color;

                // Make changes to the view. 
                for (long i = 0; i < length; i += colorSize)
                {
                    accessor.Read(i, out color);
                    color.Brighten(10);
                    accessor.Write(i, ref color);
                }
            }
        }
    }
}

public struct MyColor
{
    public short Red;
    public short Green;
    public short Blue;
    public short Alpha;

    // Make the view brigher. 
    public void Brighten(short value)
    {
        Red = (short)Math.Min(short.MaxValue, (int)Red + value);
        Green = (short)Math.Min(short.MaxValue, (int)Green + value);
        Blue = (short)Math.Min(short.MaxValue, (int)Blue + value);
        Alpha = (short)Math.Min(short.MaxValue, (int)Alpha + value);
    }
}
使用系统;
使用System.IO;
使用System.IO.MemoryAppedFile;
使用System.Runtime.InteropServices;
班级计划
{
静态void Main(字符串[]参数)
{
长偏移量=0x10000000;//256兆字节
long length=0x20000000;//512兆字节
//创建内存映射文件。
使用(var mmf=MemoryMappedFile.CreateFromFile(@“c:\ExtremelyLargeImage.data”,FileMode.Open,“ImgA”))
{
//从256MB(偏移量)创建随机访问视图
//到768MB(偏移量加长度)。
使用(var accessor=mmf.CreateViewAccessor(偏移量,长度))
{
int colorSize=Marshal.SizeOf(typeof(MyColor));
霉色;
//对视图进行更改。
用于(长i=0;i
这样的MMF不能解决您的问题。一个程序在OOM上爆炸,因为虚拟内存空间中没有足够大的洞来容纳分配。可以看出,您仍在使用MMF消耗VM地址空间

使用一个小的滑动视图是一种解决方法,但这与写入文件没有任何区别。这就是MMF在重新映射视图时所做的,它需要将脏页刷新到磁盘。简单地流式传输到文件流是合适的解决方法。如果仍然使用RAM,文件系统缓存有助于加快写入速度。如果你有一个千兆字节的RAM可用,这并不难,那么写入文件流只是一个内存到内存的拷贝。非常快,5千兆字节/秒以上。文件在后台以惰性方式写入


在Windows中,试图将数据保存在内存中是徒劳的。内存中的私有数据由分页文件支持,当Windows需要RAM用于其他进程时,将写入该文件。当您再次访问它时,请重新阅读。这很慢,你使用的内存越多,情况就越糟。与任何按需分页的虚拟内存操作系统一样,磁盘和内存之间的区别很小。

在该示例中,我看不出
存取器的创建是否是一个64k的缓冲区,例如读取512MB块,在我看来,它似乎是一个分配了512MB缓冲区。我一定完全误解了一些事情。根据我的经验,一个32位的.NET程序由于内存不足异常而爆炸,因为它达到了总管理内存使用量的~1.3-1.5Gb上限,或者(同样常见)已对LOH进行碎片化,并尝试分配一个大型阵列,例如在使用大型内存流时。如果Windows I/O将一个短期临时文件本质上作为内存中的副本,听起来不错,我将尝试一下。在使用FileStream进行此操作时,为了避免将其刷新到磁盘,我需要考虑什么?不,这是一个VM限制,非托管分配同样重要。控制FileStream数据使用了多少RAM并不取决于您。除了通过写入的数据量以及在使用缓存的机器上运行的其他进程来间接影响它之外,您无法对其产生影响。我希望使用进程之外的大量VM(对于64位操作系统中的32位应用程序),但不幸的是,我需要能够在这些数据中查找。ViewStream/VIEWACCESS的分配将在进程中分配内存,该内存将超过2GB的限制。因此,我将删除查找/缓冲要求(即具有单次传递序列化),或者使用FileStream作为临时