Важно понимать, что происходит, когда вы заполняете Hashtable. (Словарь использует Hashtable в качестве базовой структуры данных.)
Когда вы создаете новый Hashtable, .NET создает массив, содержащий 11 блоков, которые являются связанными списками словарных статей. Когда вы добавляете запись, ее ключ хэшируется, хэш-код отображается на один из 11 сегментов, а запись (ключ + значение + хэш-код) добавляется в связанный список.
В определенный момент (и это зависит от коэффициента загрузки, используемого при первом создании Hashtable), Hashtable определяет, во время операции Add, что он сталкивается с слишком большим количеством коллизий и что начальных 11 сегментов недостаточно , Таким образом, он создает новый массив сегментов, который в два раза больше старого (не совсем; количество сегментов всегда простое), а затем заполняет новую таблицу из старого.
Итак, есть две вещи, которые вступают в игру с точки зрения использования памяти.
Во-первых, Hashtable время от времени требует вдвое больше памяти, чем он использует в настоящее время, чтобы он мог копировать таблицу во время изменения размера. Так что, если у вас есть Hashtable, который использует 1,8 ГБ памяти и требует изменения размера, вкратце потребуется 3,6 ГБ, и, ну, теперь у вас проблема.
Во-вторых, каждая запись хеш-таблицы имеет около 12 байтов служебной информации: указатели на ключ, значение и следующую запись в списке, а также хеш-код. Для большинства применений эти издержки незначительны, но если вы создаете Hashtable со 100 миллионами записей, то это примерно 1,2 ГБ.
Вы можете преодолеть первую проблему, используя перегрузку конструктора Dictionary, которая позволяет вам предоставить начальную емкость. Если вы укажете емкость, достаточную для хранения всех записей, которые вы собираетесь добавить, Hashtable не нужно будет перестраивать во время заполнения. Со вторым практически ничего не поделаешь.