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

时间:2010-02-01 12:16:40

标签: c# wpf

我们正在处理大量需要打开和关闭数据的文件。 在临时散列表或其他对象中缓存每个文件的内存流是不是一个好主意?

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

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

5 个答案:

答案 0 :(得分:4)

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

如果你还没有缓存它们,请不要,因为你只会让它变得更糟。也许你有内存泄漏?你在使用后会丢弃内存流吗?

处理大型文件的最佳方法是流入和流出数据(使用FileStreams),这样就不必一次将整个文件放在内存中......

答案 1 :(得分:0)

如果文件内容缓存在常见情况下和/或具有如此少的信息,则非常不合理地说“是”或“否”。然而 - 有限的资源是真实的世界状态,而你(作为开发者)必须依赖它。如果你想要缓存一些东西,你应该使用一些机制来自动卸载数据。在.NET框架中,您可以使用WeakReference类,它卸载目标对象(字节数组和内存流也是对象)。

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

但是,你应该谦虚地使用资源(cpu,ram)并使用“廉价”的实现方式。

答案 2 :(得分:0)

我认为问题在于,完成后,文件不会立即处理,而是等待下一个GC循环。

Streams是IDisposable,这意味着您可以并且应该使用using块。然后当你完成处理时,流将会不经意地处理。

答案 3 :(得分:0)

我不认为缓存这么多数据是一个很好的解决方案,即使你没有得到任何记忆溢出。查看内存映射文件解决方案,这意味着文件位于文件系统上,但读取速度几乎等于内存中的文件(肯定会有开销)。看看这个链接。 MemoryMappedFiles

P.S。在互联网上,这是关于这个话题的非常好的文章和例子。 祝你好运。

答案 4 :(得分:0)

MemoryStream的一个问题是,每次强制容量增加时内部缓冲区的大小都会翻倍。即使您的MemoryStream为100MB且文件为101MB,只要您尝试将最后1MB写入MemoryStream,MemoryStream上的内部缓冲区就会翻倍至200MB。如果为Memory Buffer提供的启动容量等于文件的启动容量,则可以减少此值。但是这仍然允许文件使用所有内存并在加载一些文件后停止任何新的分配。如果在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();
    }
}