2010-11-02 2 views
25

Я хочу хранить 100 миллионов терминов и их частоты (в текстовой базе данных) в HashMap <String, Double>. Это дает мне ошибку «Недостаточно памяти». Я попытался увеличить кучу пространства до -Xmx15000M. Однако он проходит полчаса, а затем снова бросает одно и то же исключение. Размер файла, с которого я пытаюсь читать слова и частоты, составляет 1,7 ГБ.HashMap на Java, 100 миллионов записей

Любая помощь будет высоко оценена.

Спасибо :-)

+0

У вас работает 32-битная или 64-разрядная JVM? – nos

+22

Что вы делаете, что требует 100 миллионов терминов? Вы работаете в Google? – DJClayworth

+0

Почему вы хотите сохранить его в HashMap в первую очередь? Как многие предложили вы можете сохранить в базе данных, вы можете захотеть ее уменьшить (Hadoop?). Хотя это будет полностью зависеть от того, почему HashMap. – ch4nd4n

ответ

17

Для текстовой обработки ответ обычно является деревом, а не hashmap, если вы можете жить с более длительным временем поиска. Эта структура достаточно эффективна для памяти для естественных языков, где многие слова имеют общие начальные строки.

В зависимости от ввода, дерево Патрисии может быть еще лучше.

(Кроме того, если это действительно слова с естественного языка, вы уверены, что вам действительно нужны 100 000 000 записей? Большинство часто используемых слов удивительно низки, коммерческие решения (предсказание слов, коррекция орфографии) редко используют более 100 000 слова, независимо от языка.)

+0

Я попробовал Патрицию. На этот раз я нажимаю ограничение GC и 15 ГБ памяти все еще недостаточно. :-) – ablimit

+13

Итак, этот ответ не работает, и все же вы его приняли. – DJClayworth

+4

Он указал мне на другое решение, и я изучил новый инструмент библиотеки. Трудно выбрать лучший, тогда как все ответы указывают на что-то полезное. Лучший ответ подходит к одному, но я очень благодарен всем серьезным ответчикам здесь. Надеюсь, я смогу выбрать более одного лучшего ответа ... – ablimit

4

100 миллионов терминов вы почти наверняка за пределом того, что должно быть сохранено в памяти. Сохраните свои условия в какой-либо базе данных. Либо используйте коммерческую базу данных, либо напишите что-нибудь, что позволит вам получить доступ к файлу, чтобы получить нужную вам информацию. Если формат файла, который у вас есть, не позволяет быстро получить доступ к файлу, а затем преобразовать его в тот, который делает - например, сделать каждую запись фиксированным размером, чтобы вы могли мгновенно вычислить смещение файла для любого номера записи. Сортировка записей позволит вам быстро выполнить двоичный поиск. Вы также можете написать код, чтобы значительно ускорить доступ к файлам без необходимости хранить весь файл в памяти.

1

Тройка ThashMap использует намного меньше памяти. Тем не менее, сомневаюсь, что этого будет достаточно для уменьшения размера. Вам нужно где-то еще хранить эту информацию для извлечения, кроме строго в памяти.

+1

Пожалуйста, не рекомендуем Trove, есть лучшие варианты: http://java-performance.info/hashmap-overview-jdk-fastutil-goldman-sachs-hppc-koloboke-trove-january-2015/ – leventov

+1

@leventov Вы понимаете этот ответ шесть лет, не так ли? В то время это был хороший вариант. Хорошо иметь обновленную информацию, но просто скажите это. – AHungerArtist

+2

Я сделал заметку для будущих читателей этого сообщения. – leventov

11

Ваша проблема заключается в том, что необработанный текст 1,7 ГБ составляет более 1500 МБ, даже без накладных расходов, добавленных отдельными строковыми объектами. Для огромных сопоставлений вам нужно либо использовать базу данных, либо карту с поддержкой файлов, это будет использовать дисковое пространство вместо кучи.

Update

Я не думаю, что выделение 15 ГБ для кучи возможно для большинства JVM. Он не будет работать с любым 32-битным jvm, и я не думаю, что будет работать 64-битный jvm. 15 ГБ памяти должны работать на 64-битном jvm, когда доступно достаточное количество ОЗУ.

+0

@nos это будет до 3.4 ГБ. – josefx

+0

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

+4

@josefk: Я знаю, что это сообщение устарело, но вы можете выделить более 15 ГБ оперативной памяти для одного процесса JVM. Я пробовал его до 25 ГБ, и он работает. Технические характеристики: 64-ядерная машина с 64 ГБ оперативной памяти и Sun JDK 6. –

-1

По какой причине это не удалось, я согласен с приведенными выше ответами.

DB - хороший выбор. Но даже коммерческий уровень БД они также предложили бы «Разделение» данных для эффективного действия.

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

Узел 01 имеет ключевое начиная с «а» Узел 02 имеет ключевое starging с «B» ....

Так что ваша программа внезапно изменилась к сети программирования ..

+1

О, да ладно.100 миллионов строк - это небольшое изменение для надлежащего сервера базы данных. Нет смысла переходить сюда. У меня есть таблицы с 10-кратным количеством данных без каких-либо проблем с производительностью. – TomTom

0

Его плохой дизайн. Имея 1.7GB данных в памяти на HashMap, я сделал бы любой из двух:

  1. Упорство все данные (файл/базу данных) и иметь верхний 1% или что-то в памяти. Используйте некоторый алгоритм для определения, какие идентификаторы будут в памяти и когда.

  2. Использование memcached. Самый простой выход. Распределенная хешируемая память. Это именно то, для чего используются DHT.

1

Другие ответы уже указывали, что проблема заключается в использовании памяти. В зависимости от вашей проблемной области вы можете создать ключевой класс, который уменьшил общий объем памяти. Например, если ваш ключ состоит из фраз естественного языка, вы можете разделить и ставить слова, составляющие фразу; например

public class Phrase { 
    private final String[] interned; 

    public Phrase(String phrase) { 
    String[] tmp = phrase.split(phrase, "\\s"); 

    this.interned = new String[tmp.length]; 

    for (int i=0; i<tmp.length; ++i) { 
     this.interned[i] = tmp[i].intern(); 
    } 
    } 

    public boolean equals(Object o) { /* TODO */ } 
    public int hashCode() { /* TODO */ } 
} 

На самом деле это решение может работать, даже если строки не представляют естественного языка, обеспечивая существует значительное перекрытие, которое может быть использовано между строками.

3

Если вам нужен только легкий магазин KeyValue (Map), я бы посмотрел на Redis. Это очень быстро и имеет возможность сохранять данные в случае необходимости. Единственным недостатком является то, что вам нужно запустить хранилище Redis на Linux-машине.

Если вы ограничены Windows, MongoDB является хорошим вариантом, если вы можете запустить его на 64-битной основе.

+0

Но похоже, что redis с java немного сложнее? – ablimit

+0

См. Http://code.google.com/p/jredis/ – Joshua

+0

. Redis теперь тоже совместим с Windows :) – agpt

1

Откажитесь от HashMap и загрузите все эти данные в HBase или в один из других хранилищ данных NoSQL и напишите ваши запросы в терминах MapReduce операций. Это подход, используемый Google Search и многими другими сайтами, использующими огромные объемы данных. Он доказал, что он масштабируется до практически бесконечного размера.

+1

Говорить «в основном бесконечно» немного вводит в заблуждение: http://www.multivax.com/last_question.html –

2

Вы также можете попробовать увеличить количество дубликатов.

Например, кот = Кошки = кошки = Cat

или

плавать = плавание = проплывает

попробовать погуглить "Портер Штеммер"

0

Рассмотрим заменив его cdb. До 4 ГБ и:

Успешный поиск в большой базе данных обычно занимает всего два диска. Неудачный поиск занимает только один.

0

Существует интересное предложение от Terracotta - BigMemory, которое, кажется, именно то, что вы хотите. Я сам не пробовал и не знаю лицензионных условий и т. Д.

0

Задняя часть конверта: 1.7GB/100M = ср 18 байт = на срок и частота

Мы можем использовать handcoded HashMap при поддержке двух логических массивов.

  1. Один для хранения INT частот (значения), а другой, чтобы построить массив типа обугленного C для имитации двумерного массива гр (массив символьных массивов). поэтому мы индексируем вычисление. мы не можем использовать двумерный массив java, поскольку он имеет слишком много накладных расходов на объект. Этот массив символов может содержать массивы символов фиксированного размера для представления ключей. Поэтому мы вычисляем хэш ключа и помещаем его в этот «двумерный массив», и если у нас есть конфликт, он может быть разрешен, скажем, линейным зондированием. пары ключей и значений связаны общим индексом массивов.

  2. Хешмап должен использовать открытую адресацию, так как у нас недостаточно памяти для цепочки.

  3. Мы можем сказать, что 10 экземпляров этой хэш-карты основаны на длине ключей; не может быть уверенным, поскольку я не знаю характеристик данных.

  4. Space используется = 2 мощности 29 для целочисленного массива + (2 питания 4 (16 байт на строку) * 2 пау 27) = 3,5 гига

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

5

1,7 ГБ файл является относительно небольшим файлом для этого и хранения в ОЗУ. Я делаю это с гораздо большими файлами и сохраняю их в памяти без проблем. База данных может быть использована, но может быть чрезмерной или может быть идеальной в зависимости от того, что вы планируете делать с данными.

Как и другие люди, с естественным языком, скорее всего, будет относительно небольшое количество уникальных значений, поэтому на самом деле карта не будет настолько большой. Я бы не использовал java.util.HashMap, поскольку это - использование very inefficient in terms of memory, особенно при сохранении примитивных значений, таких как int. java.util.HashMap хранит примитивы как объекты. Он также сохраняет каждое значение внутри объекта HashMap.Entry, который отнимает память. Из-за этих двух факторов java.util.HashMap использует гораздо больше памяти, чем такие альтернативы, как Trove, Fastutil и другие:

Как уже упоминалось есть несколько реализаций карты которые не имеют этих проблем. Поскольку вы храните цифры на карте, дополнительное преимущество в том, что вы получите повышение производительности, потому что нет необходимости постоянно переключаться между объектами и примитивами (например, бокс/распаковка), поскольку вы добавляете новые значения на карту или обновляете старые значения. Эталонный различных примитивных HashMaps, которые лучше подходят для больших объемов данных можно найти on this post at the Java Performance Tuning Guide:

0

В Java, объект имеет накладные расходы на 16 байт в качестве минимального размера , прежде чем рассмотреть, что другой контент он держит.

1E8 элементов в хэш-карта имеет требование заниженное размер из 1E8 * 2 * 16 байт, и берет на себя ключи и значения являются числами, так что требуется несколько гигабайт кучи доступных в вашей куче и от твой компьютер.

Строка представляет собой объект, содержащий массив символов, так что ваши строки , как упоминалось выше, многие могут быть больше, чем двойной объект , например, следовательно, вам потребуется больше памяти, доступной для кучи.

Обратите внимание, что программы начинают плохо работать, когда вы приближаетесь к пределу вашего компьютера.

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

Смежные вопросы