примеры узких мест сбора мусора - PullRequest
2 голосов
/ 27 января 2009

Я вспомнил, как кто-то говорил мне один хороший. Но я не могу вспомнить это. Я провел последние 20 минут с Google, пытаясь узнать больше.

Каковы примеры плохого / не очень хорошего кода, который вызывает снижение производительности из-за сборки мусора?

Ответы [ 10 ]

2 голосов
/ 21 июня 2012

Каковы примеры плохого / не очень хорошего кода, который вызывает снижение производительности из-за сборки мусора?

Следующее будет неэффективным при использовании сборщика мусора поколений:

  1. Мутирование ссылок в куче, потому что барьеры записи значительно дороже, чем записи указателей. Попробуйте заменить выделение кучи и ссылки на массив типов значений и целочисленный индекс в массиве соответственно.

  2. Создание долгоживущих временных. Когда они выживают в детском поколении, они должны быть помечены, скопированы и все указатели на них обновлены. Если возможно объединить обновления для повторного использования старой версии коллекции, сделайте это.

  3. Сложные топологии кучи. Опять же, рассмотрите возможность замены многих ссылок на индексы.

  4. Стеки с глубокой резьбой. Старайтесь, чтобы стеки были мелкими, чтобы GC было легче сопоставлять глобальные корни.

Однако я бы не назвал их "плохими", потому что в них нет ничего объективного. Они неэффективны только при использовании с такого рода сборщиком мусора. При ручном управлении памятью ни одна из проблем не возникает (хотя многие заменяются эквивалентными проблемами, например производительность malloc по сравнению с распределителями пула). При других видах GC некоторые из этих проблем исчезают, например, некоторые GC не имеют барьера записи, GC области метки должны лучше обрабатывать долгоживущие временные фильтры, не всем виртуальным машинам нужны стеки потоков.

2 голосов
/ 27 января 2009

из старого солнечного технического совета - иногда это помогает явно аннулировать ссылки, чтобы сделать их пригодными для сбора мусора ранее:

public class Stack {
  private static final int MAXLEN = 10;
  private Object stk[] = new Object[MAXLEN];
  private int stkp = -1;

  public void push(Object p) {stk[++stkp] = p;}

  public Object pop() {return stk[stkp--];}
}

переписывание метода pop таким образом помогает обеспечить своевременную сборку мусора:

public Object pop() {
  Object p = stk[stkp];
  stk[stkp--] = null;
  return p;
}
1 голос
/ 27 января 2009

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

0 голосов
/ 22 сентября 2009

Я столкнулся с хорошим примером при выполнении параллельного моделирования на основе ячеек в Python. Ячейки инициализируются и отправляются рабочим процессам после выбора для запуска. Если у вас одновременно слишком много ячеек, на главном узле заканчивается оперативная память. Хитрость заключается в том, чтобы ограниченное количество ячеек упаковывало их и отправляло на узлы кластера, прежде чем делать что-то еще, не забудьте установить для объектов, уже отправленных, значение «Нет». Это позволяет выполнять большое моделирование с использованием общей оперативной памяти кластера в дополнение к вычислительной мощности.

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

0 голосов
/ 27 января 2009

Ваш пользовательский сервис не имеет ограничителя нагрузки, поэтому:

  • По какой-то причине одновременно поступает много запросов (утром все входят в систему, говорят)
  • Службе требуется больше времени для обработки каждого запроса, поскольку теперь она имеет 100 потоков (1 на запрос)
  • Тем не менее, из-за более длительного времени обработки увеличивается количество обработанных запросов.
  • Каждый обработанный частью запрос создал множество объектов, которые живут до конца обработки этого запроса.
  • Сборщик мусора тратит много времени, пытаясь освободить память, однако это не может быть сделано из-за вышеизложенного.
  • Тем не менее, большая часть обработанных запросов создается из-за более длительного времени обработки…. (включая время в GC)
0 голосов
/ 27 января 2009

В большинстве современных сборщиков любое использование финализации замедляет сборщик. И не только для объектов, которые имеют финализаторы.

0 голосов
/ 27 января 2009
  • частые выделения памяти
  • отсутствие повторного использования памяти (при работе с большими кусками памяти)
  • хранение объектов дольше необходимого (хранение ссылок на устаревшие объекты)
0 голосов
/ 27 января 2009

Я могу привести пример, который будет работать с .Net CLR GC: * ​​1001 *

Если вы переопределяете метод finalize из класса и не вызываете метод Finalize суперкласса, например



protected override void Finalize(){
    Console.WriteLine("Im done");
    //base.Finalize(); => you should call him!!!!!
}

Когда вы случайно воскрешаете объект


protected override void Finalize(){
    Application.ObjJolder = this;
}

class Application{
    static public object ObjHolder;
}

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

0 голосов
/ 27 января 2009
String foo = new String("a" + "b" + "c");

Я понимаю, что Java лучше об этом сейчас, но в первые дни это включало создание и уничтожение 3 или 4 строковых объектов.

0 голосов
/ 27 января 2009

Одним из примеров могут быть ссылки на объекты, которые хранятся в переменных-членах или статических переменных. Вот пример:

class Something {
  static HugeInstance instance = new HugeInstance();
}

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

...