简体   繁体   English

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

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

We are dealing with a lot of files which need to be opened and close for data reads mostly. 我们正在处理很多文件,大多数情况下需要打开和关闭这些文件才能读取数据。 Is it a good idea or not to cache the memorystream of each file in a temp hashtable or some other object? 将每个文件的内存流缓存在临时哈希表或其他对象中是个好主意吗?

We have noticed when opening files over 100MB we are running into out of memory exceptions. 当打开超过100MB的文件时,我们已经注意到内存不足异常。 We are using a wpf app. 我们正在使用wpf应用程序。

We could successfully open the files 1 or 2 time sometimes 3 to 4 times but after that we are running into out of memory exceptions. 我们可以成功打开文件1到2次,有时3到4次,但是之后我们遇到了内存不足的异常。

If you are currently caching these files, then you would expect to run out of memory quite quickly. 如果您当前正在缓存这些文件,那么您会期望很快耗尽内存。

If you aren't caching them yet, don't, because you'll just make it worse. 如果您尚未缓存它们,请不要这样做,因为那样只会使情况变得更糟。 Perhaps you have a memory leak? 也许您有内存泄漏? Are you disposing of the memorystream once you've used it? 使用完内存流后,您是否要对其进行处置?

The best way to deal with large files is to stream data in and out (using FileStreams), so that you don't have to have the whole file in memory at once... 处理大文件的最佳方法是使用FileStreams进行数据流的输入和输出,因此您不必一次将整个文件都保存在内存中。

It's very dificutl say "yes" or "no", if is file content caching right in the common case and/or with so little informations. 如果在正常情况下和/或信息很少的情况下缓存文件内容,则说“是”或“否”非常困难。 However - finited resources are real state of world, and you (as developer) must count with it. 但是,有限的资源是真实的世界,您(作为开发人员)必须对此加以考虑。 If you want cache something, you should use some mechanism for auto unloading data. 如果要缓存某些内容,则应使用某种机制来自动卸载数据。 In .NET framework you can use a WeakReference class, which unloads the target object (byte array and memory stream are objects too). 在.NET Framework中,您可以使用WeakReference类,该类卸载目标对象(字节数组和内存流也是对象)。

If you have the HW in you control, and you can use 64bit and have funds for very big RAM, you can cache big files. 如果您拥有硬件控制权,并且可以使用64位并且有很大的RAM资金,则可以缓存大文件。

However, you should be humble to resources (cpu,ram) and use the "cheap" way of implementation. 但是,您应该对资源(cpu,ram)保持谦逊,并使用“便宜”的实现方式。

I think that the problem is that after you are done, the file is not disposed immediatly, it is waiting to the next GC cycle. 我认为问题在于,完成后,该文件没有立即处置,它正在等待下一个GC周期。

Streams are IDisposable, whice means you can and should use the using block. 流是IDisposposable的,这意味着您可以并且应该使用using块。 then the stream will dispose immidiatly when your are done dealing with it. 那么当您处理完该流后,该流将立即处理。

I don't think that caching such amount of data is a good solution, even if you don't get ever memroy overflow. 我不认为缓存这样的数据量不是一个好的解决方案,即使您不会出现内存溢出。 Check out Memory Mapped files solution, which means that file lays on file system but speed of reading is almost equal to the in memory ones (there is an overhead for sure). 签出“内存映射文件”解决方案,这意味着文件位于文件系统上,但读取速度几乎等于内存中的速度(肯定会有开销)。 Check out this link. 查看此链接。 MemoryMappedFiles MemoryMappedFiles

PS Ther are pretty good articles and examples on this topic arround in internet. PS Ther是关于互联网上这个主题的相当不错的文章和示例。 Good Luck. 祝好运。

One issue with the MemoryStream is the internal buffer doubles in size each time the capacity is forced to increase. MemoryStream的一个问题是,每次强制增加容量时,内部缓冲区的大小都会加倍。 Even if your MemoryStream is 100MB and your file is 101MB, as soon as you try to write that last 1MB to the MemoryStream the internal buffer on MemoryStream is doubled to 200MB. 即使您的MemoryStream是100MB,文件是101MB,只要您尝试将最后1MB写入MemoryStream,MemoryStream上的内部缓冲区就会加倍到200MB。 You may reduce this if you give the Memory Buffer a starting capacity equal to that of you files. 如果为内存缓冲区提供的启动容量等于文件的启动容量,则可以减少此开销。 But this will still allow the files to use all of the memory and stop any new allocations after the some of the files are loaded. 但这仍将允许文件使用所有内存,并在某些文件加载​​后停止任何新分配。 If create a cache object that is help inside of a WeakReference object you would be able to allow the garbage collector to toss a few of your cached files as needed. 如果创建一个在WeakReference对象内提供帮助的缓存对象,您将能够允许垃圾收集器根据需要抛弃一些缓存文件。 But don't forget you will need to add code to recreate the lost cache on demand. 但是不要忘记,您将需要添加代码以根据需要重新创建丢失的缓存。

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