Как неустранимые объекты помогают снизить накладные расходы из-за коллекции мусора?

Я новичок, и я прочитал о сборнике мусора из первых двух ответов здесь.

Теперь оправдываем использование неизменяемых объектов, даже если программисту необходимо создавать новые объекты по сравнению с использованием существующих объектов (в многопоточных приложениях), в этом руководстве говорится, что стоимость создания объекта компенсируется уменьшением издержек памяти из-за сбоя мусора и устранением кода для защиты изменяемых объектов от помех потоков и ошибок согласованности памяти:

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

Вопрос в том, как? Что представляет собой сборка мусора, связанная с изменчивостью или неизменностью объектов?

Ответ 1

Иногда вы выделяете меньше, когда объекты неизменяемы.

Простой пример

 Date getDate(){
   return copy(this.date);
 }

Мне нужно копировать Date каждый раз, когда я обмениваюсь им, потому что он изменен или вызывающий может изменить его. Если getDate получится много, скорость распределения резко возрастет, и это окажет давление на GC

С другой стороны, даты Java-8 неизменяемы

LocalDate getDate(){
  return this.date;
}

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

Теперь вы можете подумать, как я могу применить это к "полезным" или сложным структурам данных, не вызывая массивного выделения (из-за защитных копий), вы абсолютно правы, но есть искусство под названием functional programming и persistent data structures ( т.е.: вы получаете иллюзию, что это новая копия, где на самом деле копии имеют много общего с оригиналом).

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

Ответ 2

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

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

Реальный вопрос заключается в том, делаете ли вы много чтений или много записей (или смесей). В зависимости от использования. Неизменяемые объекты могут сохранять объекты или создавать больше объектов, поэтому имеет смысл использовать либо объект Immutable, либо Mutable на основе ваш конкретный вариант использования.

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

Ответ 3

В в этой статье Брайан Гетц объясняет это красиво. В основном, это связано с тем, как работает сборщик мусора. У него меньше работы, если новые объекты ссылаются на старые, а наоборот.

Выдержка из связанной статьи с примерами ниже:

public class MutableHolder {
  private Object value;
  public Object getValue() { return value; }
  public void setValue(Object o) { value = o; }
}

public class ImmutableHolder {
  private final Object value;
  public ImmutableHolder(Object o) { value = o; }
  public Object getValue() { return value; }
}

В большинстве случаев, когда объект держателя обновляется для ссылки на другой объект, новый референт - молодой объект. Если мы обновим MutableHolder, вызывая setValue(), мы создали ситуацию где более старый объект ссылается на младшего. С другой стороны, создавая вместо этого новый объект ImmutableHolder, младший объект ссылаясь на более старый.

Последняя ситуация, когда большинство объектов указывает на старые объекты, гораздо более мягким на коллекционере мусора. Если MutableHolder, который живет в старом поколении, мутирован, все объекты на карте, содержащие MutableHolder, должны быть отсканированы для ссылок от старых к молодым в следующей небольшой коллекции.

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

Анализ Escape

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

Взгляните на анализ утечки (также упоминается в связанной статье). Многие объекты не будут выделены в куче (но будут включены/выделены в стеке), поэтому GC не будет иметь к ним никакого отношения (на самом деле GC не знает, что эти объекты существуют вообще).

Хотя это не связано только с неизменяемостью, механизм анализа эвакуации может быть использован более эффективно в неизменяемом контексте (примером является объект Person, который не изменяется во время вызова метода в связанных документах Oracle).