Можете ли вы использовать List <List <struct>>, чтобы обойти ограничение в 2 ГБ? - PullRequest
17 голосов
/ 27 марта 2012

Я столкнулся с лимитом объекта 2 ГБ в c # (это применимо даже в 64-битной среде по какой-то раздражающей причине) с большим набором структур (суммарный размер 4,2 гигабайта).

Теперь очевидно, что использование List даст мне список размером 4.2Gb, который можно сдавать или брать, но будет ли использование списка, состоящего из меньших списков, которые в свою очередь содержат часть структур, позволить мне преодолеть этот предел?

Мое рассуждение здесь заключается в том, что это только жестко заданное ограничение в CLR, которое мешает мне создавать экземпляр 9-гигабайтного объекта на моей 64-битной платформе, и оно совершенно не связано с системными ресурсами.Списки и массивы также являются ссылочными типами, поэтому список, содержащий списки, будет фактически содержать только ссылки на каждый список.Поэтому ни один объект не превышает ограничение по размеру.

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

Ответы [ 5 ]

13 голосов
/ 27 марта 2012

Теперь, очевидно, использование List даст мне список размером 4.2gb, который можно сдавать или брать, но позволит ли мне преодолеть этот предел, если использовать список, состоящий из небольших списков, которые, в свою очередь, содержат часть структур?

Да - хотя, если вы пытаетесь обойти это ограничение, я бы подумал о том, чтобы использовать массивы самостоятельно, а не позволять классу List<T> управлять массивом.

Ограничение в 2 ГБ для одного объекта в CLR - это как раз один экземпляр объекта. Когда вы создаете массив структуры (который List<T> использует для внутреннего использования), весь массив представляет собой «один экземпляр объекта» в CLR. Однако, используя List<List<T>> или зубчатый массив, каждый внутренний список / массив является отдельным объектом, который позволяет эффективно иметь любой объект размера, который вы пожелаете.

Команда CLR фактически написала об этом в блоге и предоставила образец BigArray<T> реализацию, которая действует как один List<T>, но выполняет внутреннее "блокирование" для вас. Это еще один вариант для получения> 2 ГБ списков.

Обратите внимание, что .NET 4.5 будет иметь возможность предоставлять объекты размером более 2 ГБ в x64 , но это будет то, что вы должны явно указать.

2 голосов
/ 27 марта 2012

List содержит ссылки размером 4 или 8 байтов, в зависимости от того, работаете ли вы в 32-битном или 64-битном режиме, поэтому если вы ссылаетесь на объект объемом 2 ГБ, который не увеличит фактический размер Listдо 2 ГБ, но это только увеличит его на количество байтов, необходимых для ссылки на этот объект.

Это позволит вам ссылаться на миллионы объектов, и каждый объект может иметь размер 2 ГБ.Если у вас есть 4 объекта в List и каждый по 2 ГБ, то у вас будет 8 ГБ объектов, на которые ссылается List, но объект List израсходовал бы только дополнительные 4 * 8 = 32байт.

Количество ссылок, которые можно хранить на 32-разрядной машине до того, как List достигнет ограничения в 2 ГБ, составляет 536,87 миллиона, на 64-разрядной машине - 268,43 миллиона.

536 миллионов ссылок * 2 ГБ = МНОГО ДАННЫХ!

PS Рид отметил, что приведенное выше верно для ссылочных типов, но не для типов значений.Так что если вы держите типы значений, то ваш обходной путь действителен.Пожалуйста, смотрите комментарий ниже для получения дополнительной информации.

0 голосов
/ 09 марта 2018
class HugeList<T>
{
    private const int PAGE_SIZE = 102400;
    private const int ALLOC_STEP = 1024;

    private T[][] _rowIndexes;

    private int _currentPage = -1;
    private int _nextItemIndex = PAGE_SIZE;

    private int _pageCount = 0;
    private int _itemCount = 0;

    #region Internals

    private void AddPage()
    {
        if (++_currentPage == _pageCount)
            ExtendPages();

        _rowIndexes[_currentPage] = new T[PAGE_SIZE];
        _nextItemIndex = 0;
    }

    private void ExtendPages()
    {
        if (_rowIndexes == null)
        {
            _rowIndexes = new T[ALLOC_STEP][];
        }
        else
        {
            T[][] rowIndexes = new T[_rowIndexes.Length + ALLOC_STEP][];

            Array.Copy(_rowIndexes, rowIndexes, _rowIndexes.Length);

            _rowIndexes = rowIndexes;
        }

        _pageCount = _rowIndexes.Length;
    }

    #endregion Internals

    #region Public

    public int Count
    {
        get { return _itemCount; }
    }

    public void Add(T item)
    {
        if (_nextItemIndex == PAGE_SIZE)
            AddPage();

        _itemCount++;
        _rowIndexes[_currentPage][_nextItemIndex++] = item;
    }

    public T this[int index]
    {
        get { return _rowIndexes[index / PAGE_SIZE][index % PAGE_SIZE]; }
        set { _rowIndexes[index / PAGE_SIZE][index % PAGE_SIZE] = value; }
    }

    #endregion Public
}
0 голосов
/ 26 июня 2012

В версиях .NET до 4.5 максимальный размер объекта составляет 2 ГБ.Начиная с версии 4.5, вы можете выделять более крупные объекты, если gcAllowVeryLargeObjects включен.Обратите внимание, что ограничение для string не затрагивается, но «массивы» должны также охватывать «списки», поскольку списки поддерживаются массивами.

0 голосов
/ 27 марта 2012

Здесь есть интересный пост на эту тему:

http://blogs.msdn.com/b/joshwil/archive/2005/08/10/450202.aspx

, в котором говорится о написании вашего собственного объекта 'BigArray'.

...