Почему мой хэш-сет занимает столько памяти? - PullRequest
3 голосов
/ 07 ноября 2011

Я обнаружил, что объем памяти моей программы увеличивается из-за приведенного ниже кода, в настоящее время я читаю файл размером около 7 ГБ, и я считаю, что файл, который будет храниться в хэш-наборе, меньше 10 МБ, но память моя программа продолжает увеличиваться до 300 МБ, а затем падает из-за OutofMemoryError. Если это проблема Hashset, какую структуру данных мне выбрать?

    if(tagsStr!=null) {
        if(tagsStr.contains("a")||tagsStr.contains("b")||tagsStr.contains("c")) {
            maTable.add(postId);
        }
    } else {
        if(maTable.contains(parentId)) {
            //do sth else, no memories added here
        }
    }

Ответы [ 4 ]

7 голосов
/ 07 ноября 2011

Вы действительно не сказали нам, что делаете, но:

  • Если ваш файл в настоящее время находится в чем-то вроде ASCII, каждый прочитанный вами символ будет одним байтом в файле или двумя байтами в памяти.
  • Каждая строка будет содержать служебные данные - это может быть важно, если вы храните много маленьких строк
  • Если вы читаете строки с BufferedReader (или берете подстроки из больших строк), каждая из них может иметь большой резервный буфер - вы можете использовать maTable.add(new String(postId)), чтобы избежать этого
  • Каждая запись в хэш-наборе нуждается в отдельном объекте для хранения значений ключа / хэш-кода / значения / следующей записи. Опять же, с большим количеством записей это может составить

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

3 голосов
/ 07 ноября 2011

У вас либо утечка памяти, либо вы неверно понимаете объем хранимых строковых данных. Мы не можем сказать, что, не видя больше вашего кода.

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


Если бы я догадался, это было бы, что ваше приложение (на некотором уровне) делает что-то вроде этого:

String line;
while ((line = br.readLine()) != null) {
    // search for tag in line
    String tagStr = line.substring(pos1, pos2);
    // code as per your example
}

Это использует намного больше памяти, чем вы ожидаете. Вызов substring(...) создает объект tagStr, который ссылается на базовый массив исходной строки line. Ваши строки тегов, которые, как вы ожидаете, будут короткими на самом деле , ссылаются на char[] объект, который содержит все символы в исходной строке.

Исправление заключается в следующем:

    String tagStr = new String(line.substring(pos1, pos2));

Это создает объект String, который не использует общий массив аргумента String.

ОБНОВЛЕНИЕ - это или что-то подобное становится все более вероятным объяснением ... учитывая ваши последние данные.


Если говорить о другом аспекте Джона Скита, то накладные расходы небольшой строки удивительно высоки. Например, на типичной 32-битной JVM использование памяти односимвольной строки:

  • Заголовок объекта String для объекта String: 2 слова
  • Поля строкового объекта: 3 слова
  • Заполнение: 1 слово (я думаю)
  • Заголовок объекта резервного массива: 3 слова
  • Данные резервного массива: 1 слово

Всего: 10 слов - 40 байтов - для хранения одного char данных ... или одного byte данных, если ваш ввод находится в 8-битном наборе символов.

(Этого недостаточно, чтобы объяснить вашу проблему, но вы все равно должны об этом знать.)

0 голосов
/ 07 ноября 2011

Запустите вашу программу с -XX: + HeapDumpOnOutOfMemoryError . После этого вы сможете использовать анализатор памяти, такой как MAT , чтобы увидеть, что занимает всю память - это может быть что-то совершенно неожиданное.

0 голосов
/ 07 ноября 2011

Не может ли быть так, что данные, считанные в память (из файла 7G), каким-то образом не освобождаются? Что-то, что Джон пишет ... то есть. поскольку строки неизменяемы, для каждой считываемой строки требуется создание нового объекта String, что может привести к нехватке памяти, если GC недостаточно быстр ...

Если вышеприведенное относится к случаю, вы можете вставить некоторые «точки останова» в ваш код / ​​итерацию, т.е. в определенных точках введите gc и дождитесь его завершения.

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