Память Java: объект потребляет в три раза больше необходимого размера - PullRequest
2 голосов
/ 12 марта 2012

Рассмотрим класс со статическим фабричным методом, который получает строку CSV (или TSV) в качестве входных данных (имена переменных для удобства переименованы):

String[] fields=StringUtils.split(tsvLine, '\t');
return new MYObject(
    Integer.parseInt(fields[0]),
    StringUtils.strip(fields[1], "\"").intern(), // Many duplicates
    StringUtils.strip(fields[2], "\""),          // Unique
    StringUtils.strip(fields[4], "\"").intern(), // Many duplicates
    Double.parseDouble(fields[7]),
    Double.parseDouble(fields[6])); 

Этот метод анализирует около 5 миллионов записей из файла размером ~ 500 МБ. Чтобы сохранить память, я сохраняю три соединенные строки:

Я пробовал следующую оптимизацию:

public MyObject(int i1, String str0, String str1, String str2, 
                double d1, double d2)
{
...
this.tsvStrings = (str0+'\t'+str1+'\t'+str2).toCharArray();
...
}

(Разумеется, они разбиты на соответствующие методы получения и установки).

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

РЕДАКТИРОВАТЬ : str0 и str2 имеют дубликаты, str1 уникален.

Ответы [ 3 ]

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

Если у вас есть файл с кодировкой UTF-8, он будет использовать примерно вдвое больше памяти в памяти (так как он использует UTF-16 в памяти). Это потому, что String и StringBuilder используют два байта на символ (для большинства символов)

Если вы манипулируете этими данными, вам может понадобиться удвоить или более этот объем памяти.

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

1 голос
/ 12 марта 2012

У меня была такая же проблема, как и у вас.Я перепробовал много оптимизаций, таких как использование массивов char [] вместо Strings и так далее.Наконец, я перестал использовать Strings и массивы почти везде.Вместо этого я создал простой кэш слов:

com.google.common.collect.BiMap<Integer, String> stringCache = ... 
//you can use 2 java.util.HashMaps instead

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

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

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

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

Как говорили другие до меня, вы можете использовать какую-то схему сжатия, чтобы сэкономить память (возможно, посмотрите на схему кода Хаффмана или что-то подобное), хотя это может очень быстро усложниться - это может не стоитьусилия по написанию кода, если это не код производственного уровня.

Другая вещь, которую вы могли бы сделать с гораздо более быстрыми результатами, зависит от того, является ли текст, который вы читаете, Unicode или ASCII.Unicode занимает 16 бит для каждого символа, и char переменные в Java занимают 16 бит каждая из-за этого факта.Однако, если ваш текстовый файл хранится с использованием ASCII или если вы знаете, что файл не содержит символов, не поддерживаемых ASCII, вы можете привести каждое char чтение к byte и использовать byte[] вместо * 1007.*.Это может потенциально исключить до половины вашего текущего использования памяти.

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