繁体   English   中英

在缓存中使用MemoryStream时出现内存不足异常

[英]Out of memory exceptions when using MemoryStream in cache

我们正在处理很多文件,大多数情况下需要打开和关闭这些文件才能读取数据。 将每个文件的内存流缓存在临时哈希表或其他对象中是个好主意吗?

当打开超过100MB的文件时,我们已经注意到内存不足异常。 我们正在使用wpf应用程序。

我们可以成功打开文件1到2次,有时3到4次,但是之后我们遇到了内存不足的异常。

如果您当前正在缓存这些文件,那么您会期望很快耗尽内存。

如果您尚未缓存它们,请不要这样做,因为那样只会使情况变得更糟。 也许您有内存泄漏? 使用完内存流后,您是否要对其进行处置?

处理大文件的最佳方法是使用FileStreams进行数据流的输入和输出,因此您不必一次将整个文件都保存在内存中。

如果在正常情况下和/或信息很少的情况下缓存文件内容,则说“是”或“否”非常困难。 但是,有限的资源是真实的世界,您(作为开发人员)必须对此加以考虑。 如果要缓存某些内容,则应使用某种机制来自动卸载数据。 在.NET Framework中,您可以使用WeakReference类,该类卸载目标对象(字节数组和内存流也是对象)。

如果您拥有硬件控制权,并且可以使用64位并且有很大的RAM资金,则可以缓存大文件。

但是,您应该对资源(cpu,ram)保持谦逊,并使用“便宜”的实现方式。

我认为问题在于,完成后,该文件没有立即处置,它正在等待下一个GC周期。

流是IDisposposable的,这意味着您可以并且应该使用using块。 那么当您处理完该流后,该流将立即处理。

我不认为缓存这样的数据量不是一个好的解决方案,即使您不会出现内存溢出。 签出“内存映射文件”解决方案,这意味着文件位于文件系统上,但读取速度几乎等于内存中的速度(肯定会有开销)。 查看此链接。 MemoryMappedFiles

PS Ther是关于互联网上这个主题的相当不错的文章和示例。 祝好运。

MemoryStream的一个问题是,每次强制增加容量时,内部缓冲区的大小都会加倍。 即使您的MemoryStream是100MB,文件是101MB,只要您尝试将最后1MB写入MemoryStream,MemoryStream上的内部缓冲区就会加倍到200MB。 如果为内存缓冲区提供的启动容量等于文件的启动容量,则可以减少此开销。 但这仍将允许文件使用所有内存,并在某些文件加载​​后停止任何新分配。 如果创建一个在WeakReference对象内提供帮助的缓存对象,您将能够允许垃圾收集器根据需要抛弃一些缓存文件。 但是不要忘记,您将需要添加代码以根据需要重新创建丢失的缓存。

public class CacheStore<TKey, TCache>
{
    private static object _lockStore = new object();
    private static CacheStore<TKey, TCache> _store;
    private static object _lockCache = new object();
    private static Dictionary<TKey, TCache> _cache =
                                            new Dictionary<TKey, TCache>();

    public TCache this[TKey index]
    {
        get
        {
            lock (_lockCache)
            {
                if (_cache.ContainsKey(index))
                    return _cache[index];
                return default(TCache);
            }
        }
        set
        {
            lock (_lockCache)
            {
                if (_cache.ContainsKey(index))
                    _cache.Remove(index);
                _cache.Add(index, value);
            }
        }
    }

    public static CacheStore<TKey, TCache> Instance
    {
        get
        {
            lock (_lockStore)
            {
                if (_store == null)
                    _store = new CacheStore<TKey, TCache>();
                return _store;
            }
        }
    }
}
public class FileCache
{
    private WeakReference _cache;
    public FileCache(string fileLocation)
    {
        if (!File.Exists(fileLocation))
            throw new FileNotFoundException("fileLocation", fileLocation);
        this.FileLocation = fileLocation;
    }
    private MemoryStream GetStream()
    {
        if (!File.Exists(this.FileLocation))
            throw new FileNotFoundException("fileLocation", FileLocation);
        return new MemoryStream(File.ReadAllBytes(this.FileLocation));
    }

    public string FileLocation { get; private set; }
    public MemoryStream Data
    {
        get
        {
            if (_cache == null)
                _cache = new WeakReference(GetStream(), false);

            var ret = _cache.Target as MemoryStream;
            if (ret == null)
            {
                Recreated++;
                ret = GetStream();
                _cache.Target = ret;
            }
            return ret;
        }
    }

    public int Recreated { get; private set; }
}

class Program
{
    static void Main(string[] args)
    {
        var cache = CacheStore<string, FileCache>.Instance;

        var fileName = @"c:\boot.ini";
        cache[fileName] = new FileCache(fileName);

        var ret = cache[fileName].Data.ToArray();
        Console.WriteLine("Recreated {0}", cache[fileName].Recreated);
        Console.WriteLine(Encoding.ASCII.GetString(ret));
        GC.Collect();
        var ret2 = cache[fileName].Data.ToArray();
        Console.WriteLine("Recreated {0}", cache[fileName].Recreated);
        Console.WriteLine(Encoding.ASCII.GetString(ret2));
        GC.Collect();
        var ret3 = cache[fileName].Data.ToArray();
        Console.WriteLine("Recreated {0}", cache[fileName].Recreated);
        Console.WriteLine(Encoding.ASCII.GetString(ret3));
        Console.Read();
    }
}

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM