Недостаточно памяти при использовании MemoryStream в кеше - PullRequest
0 голосов
/ 01 февраля 2010

Мы имеем дело с большим количеством файлов, которые нужно открывать и закрывать, в основном, для чтения данных. Это хорошая идея или нет кэшировать поток памяти каждого файла во временную хеш-таблицу или какой-то другой объект?

Мы заметили, что при открытии файлов размером более 100 МБ мы сталкиваемся с нехваткой памяти. Мы используем приложение wpf.

Мы могли бы успешно открыть файлы 1 или 2 раза, иногда 3-4 раза, но после этого у нас возникают исключения из-за недостатка памяти.

Ответы [ 5 ]

4 голосов
/ 01 февраля 2010

Если вы в настоящее время кэшируете эти файлы, вы можете ожидать, что нехватка памяти будет довольно быстро.

Если вы еще не кешируете их, не делайте этого, потому что вы только ухудшите их. Возможно, у вас есть утечка памяти? Вы утилизируете поток памяти, когда используете его?

Лучший способ справиться с большими файлами - это потоковая передача данных (используя FileStreams), чтобы вам не приходилось хранить весь файл в памяти сразу ...

0 голосов
/ 01 февраля 2010

Одна проблема с MemoryStream заключается в том, что внутренний буфер увеличивается в два раза при каждом увеличении емкости. Даже если ваш MemoryStream составляет 100 МБ, а ваш файл - 101 МБ, как только вы попытаетесь записать последние 1 МБ в MemoryStream, внутренний буфер в MemoryStream удваивается до 200 МБ. Вы можете уменьшить это, если вы дадите буферу памяти начальную емкость, равную емкости ваших файлов. Но это все равно позволит файлам использовать всю память и прекратить любые новые выделения после загрузки некоторых файлов. Если создать объект кэша, который является справкой внутри объекта 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();
    }
}
0 голосов
/ 01 февраля 2010

Я не думаю, что кэширование такого количества данных - хорошее решение, даже если вы никогда не переполняете память. Ознакомьтесь с решением для файлов с отображением в памяти. Это означает, что файл лежит в файловой системе, но скорость чтения почти равна памяти в памяти (наверняка есть издержки). Проверьте эту ссылку. MemoryMappedFiles

P.S. В интернете есть довольно хорошие статьи и примеры на эту тему. Удачи.

0 голосов
/ 01 февраля 2010

Я думаю, что проблема в том, что после того, как вы закончите, файл не удаляется сразу, он ожидает следующего цикла GC.

Потоки IDisposable, что означает, что вы можете и должны использовать блок using. тогда поток будет немедленно уничтожен, когда вы закончите с ним работать.

0 голосов
/ 01 февраля 2010

Очень сложно сказать «да» или «нет», если в общем случае правильно кэшировать содержимое файла и / или с таким небольшим количеством информации. Однако ограниченные ресурсы - это реальное состояние мира, и вы (как разработчик) должны с этим считаться. Если вы хотите что-то кэшировать, вы должны использовать какой-то механизм для автоматической выгрузки данных. В .NET Framework вы можете использовать класс WeakReference, который выгружает целевой объект (байтовый массив и поток памяти также являются объектами).

Если у вас есть HW под вашим контролем, вы можете использовать 64-битную версию и иметь средства для очень большой оперативной памяти, вы можете кэшировать большие файлы.

Однако вы должны быть скромны с ресурсами (ЦП, ОЗУ) и использовать «дешевый» способ реализации.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...