简体   繁体   中英

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. We are using a wpf app.

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.

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...

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).

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.

However, you should be humble to resources (cpu,ram) and use the "cheap" way of implementation.

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.

Streams are IDisposable, whice means you can and should use the using block. 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

PS Ther are pretty good articles and examples on this topic arround in internet. Good Luck.

One issue with the MemoryStream is the internal buffer doubles in size each time the capacity is forced to increase. 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. 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. 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();
    }
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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