Когда лог показывает много попаданий GC, какое изменение кода нам нужно? - PullRequest
2 голосов
/ 17 мая 2010

Когда в журнале отображается много обращений к сборщику мусора, какое изменение кода нам понадобится?
Нужно ли освобождать некоторые объекты?
Ускорим ли мы код с повторным использованием объекта?

EDIT

Я запускаю этот код для множества имен:

 public static String removeAccents(String s) {
        if (s == null)
            return null;
        StringBuilder sb = new StringBuilder();
        int n = s.length();
        for (int i = 0; i < n; i++) {
            char c = s.charAt(i);
            int pos = UNICODE.indexOf(c);
            if (pos > -1) {
                sb.append(PLAIN_ASCII.charAt(pos));
            } else {
                sb.append(c);
            }
        }
        return sb.toString();
    }

EDIT2

журнал ГК

05-17 14:05:07.629: DEBUG/dalvikvm(8823): GC freed 13344 objects / 523736 bytes in 73ms
05-17 14:05:08.269: DEBUG/dalvikvm(8823): GC freed 13341 objects / 524608 bytes in 72ms
05-17 14:05:08.889: DEBUG/dalvikvm(8823): GC freed 13302 objects / 525112 bytes in 72ms
05-17 14:05:09.519: DEBUG/dalvikvm(8823): GC freed 13151 objects / 524360 bytes in 72ms
05-17 14:05:10.089: DEBUG/dalvikvm(8823): GC freed 13377 objects / 524384 bytes in 71ms
05-17 14:05:10.779: DEBUG/dalvikvm(8823): GC freed 13137 objects / 523872 bytes in 72ms
05-17 14:05:11.389: DEBUG/dalvikvm(8823): GC freed 13289 objects / 524656 bytes in 72ms
05-17 14:05:12.049: DEBUG/dalvikvm(8823): GC freed 13113 objects / 524336 bytes in 71ms
05-17 14:05:12.299: DEBUG/dalvikvm(4864): GC freed 206 objects / 10216 bytes in 358ms
05-17 14:05:12.769: DEBUG/dalvikvm(8823): GC freed 13289 objects / 524272 bytes in 75ms
05-17 14:05:13.449: DEBUG/dalvikvm(8823): GC freed 13165 objects / 524192 bytes in 68ms
05-17 14:05:14.099: DEBUG/dalvikvm(8823): GC freed 13221 objects / 524016 bytes in 73ms
05-17 14:05:14.719: DEBUG/dalvikvm(8823): GC freed 13179 objects / 524768 bytes in 73ms
05-17 14:05:15.349: DEBUG/dalvikvm(8823): GC freed 13306 objects / 524328 bytes in 73ms
05-17 14:05:15.999: DEBUG/dalvikvm(8823): GC freed 13280 objects / 523536 bytes in 73ms
05-17 14:05:16.589: DEBUG/dalvikvm(8823): GC freed 13314 objects / 524928 bytes in 68ms
05-17 14:05:17.249: DEBUG/dalvikvm(8823): GC freed 13217 objects / 524792 bytes in 73ms
05-17 14:05:17.929: DEBUG/dalvikvm(8823): GC freed 13176 objects / 524104 bytes in 68ms
05-17 14:05:18.449: DEBUG/dalvikvm(9926): GC freed 10341 objects / 558184 bytes in 488ms
05-17 14:05:18.689: DEBUG/dalvikvm(8823): GC freed 13485 objects / 524664 bytes in 75ms
05-17 14:05:19.279: DEBUG/dalvikvm(8823): GC freed 13337 objects / 523816 bytes in 67ms
05-17 14:05:19.909: DEBUG/dalvikvm(8823): GC freed 13269 objects / 524784 bytes in 72ms
05-17 14:05:20.419: DEBUG/dalvikvm(8823): GC freed 13389 objects / 524416 bytes in 72ms
05-17 14:05:21.069: DEBUG/dalvikvm(8823): GC freed 12948 objects / 523712 bytes in 72ms
05-17 14:05:21.659: DEBUG/dalvikvm(8823): GC freed 13436 objects / 525040 bytes in 68ms

Считаете ли вы, что это слишком много?

Ответы [ 4 ]

4 голосов
/ 17 мая 2010

Частая сборка мусора может быть вызвана несколькими причинами. Например:

  • Возможно, ваше приложение создает слишком много временных объектов.

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

  • Куча может быть слишком маленькой.

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

Третью проблему можно увидеть, изучив журналы GC, и заметив, что при каждом запуске GC удается только восстановить относительно небольшой объем памяти. В идеале вы хотите, чтобы сборщик мусора возвращал 50% или более кучи каждый раз при запуске. Исправление обычно заключается в увеличении максимального размера кучи с помощью параметра командной строки -Xmx JVM.

Ускорим ли мы код с повторным использованием объекта?

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

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

РЕДАКТИРОВАТЬ

Один из способов уменьшить использование памяти для кода в редактируемом вопросе - изменить:

   StringBuilder sb = new StringBuilder();

до

   StringBuilder sb = new StringBuilder(s.length());

Это может также помочь в повторном использовании StringBuilder, но если этот код дает вам чрезмерную скорость GC, проблема, скорее всего, заключается в том, что есть утечка памяти (где-то еще в вашем приложении) или ваша куча слишком маленький. (Люди не понимают этого, но для каждой строки Java существуют значительные накладные расходы памяти ... что-то вроде 48 байтов, если моя арифметика верна).

РЕДАКТИРОВАТЬ 2

Журналы GC говорят, что вы восстанавливаете 50000Kb при каждом запуске GC, и они настоятельно рекомендуют, чтобы использование памяти не увеличивалось. (Последнее является хорошей новостью; в значительной степени это исключает утечку памяти.) Я думаю, вам нужно увеличить размер кучи, используя опции -Xmx и -Xms. Вы хотите использовать несколько мегабайт в каждом цикле GC, чтобы уменьшить средние издержки GC на возвращаемый байт.

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

1 голос
/ 17 мая 2010

Ваш код генерирует много распределенных недолговечных объектов в куче. Это мечта GC: GC оптимизирован для решения именно этой ситуации. Неудивительно, что вы видите много вызовов GC, но это нормально, ожидаемо и не делает ваш код медленным. В файле журнала вы можете видеть, что каждый запуск GC занимает около 70 мс и происходит два раза в секунду; это максимум 14% вашего времени. Другими словами, даже если чудом повторного использования объектов вы удалите все динамические выделения, вы получите ускорение не более чем на 16%.

Если есть что-то, что замедляет ваш код, вот эта строка:

int pos = UNICODE.indexOf(c);

и это не имеет никакого отношения к ГХ. Эта строка выполняет линейный поиск в строке UNICODE (я предполагаю, что это экземпляр String), и она, вероятно, будет вычислительно дорогой (я предполагаю, что указанная строка несколько большая).

Я предлагаю вам попробовать заменить эту строку на:

int pos = (c <= 126) ? -1 : UNICODE.indexOf(c);

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

Для более полной обработки удаления акцента используйте java.text.Normalizer (с формой NFKD), затем для каждой результирующей кодовой точки получите ее категорию (с Character.getType()) и отбросьте все кодовые точки, которые имеют категорию COMBINING_SPACING_MARK, ENCLOSING_MARK и NON_SPACING_MARK. Это обработало бы весь Юникод во всей его красе, но, вероятно, стоило бы дороже.

1 голос
/ 17 мая 2010

Если GC часто запускается, это хороший признак того, что создается много временных объектов. Это значительно снижает производительность приложения, поскольку сборщик мусора пытается очистить объекты без ссылок в куче. Чтобы противостоять этому, вы можете профилировать приложение и выяснить моменты улучшения. Примером создания множества временных объектов может быть следующий фрагмент кода:

String str = "";    
for(int i=0;i<1000000;i++){
   str = str + String.valueOf(i);
}

Чтобы избежать создания большого количества объектов, вы можете заменить приведенный выше фрагмент кода на StringBuffer / StringBuilder.

0 голосов
/ 17 мая 2010

это может быть int pos создание / уничтожение, которое вызывает GC.

Попробуйте объявить его вне цикла for и просто сбросить его на -1 в качестве первой строки внутри цикла for.

 public static String removeAccents(String s) { 
    if (s == null) 
        return null; 
    StringBuilder sb = new StringBuilder(); 
    int n = s.length(); 
    int pos = -1;
    for (int i = 0; i < n; i++) { 
        pos = -1; //set it here just in case.
        char c = s.charAt(i); 
        pos = UNICODE.indexOf(c); 
        if (pos > -1) { 
            sb.append(PLAIN_ASCII.charAt(pos)); 
        } else { 
            sb.append(c); 
        } 
    } 
    return sb.toString(); 
} 

То же самое касается char c, создайте его вне цикла for и переустанавливайте в нем каждый раз, когда вам это нужно.

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