Как предотвратить циклические ссылки с внедрением зависимостей и сборкой мусора?

#java #actionscript-3 #oop #dependency-injection #garbage-collection

#java #actionscript-3 #ооп #внедрение зависимостей #сборка мусора

Вопрос:

Я думаю, что я все еще пытаюсь понять внедрение зависимостей и роль контейнера DI.

Если DI означает, что компонент более низкого уровня зависит от компонента более высокого уровня, и нет циклической ссылки, не будет ли этот объект собирать мусор? Насколько я понимаю сборку мусора (mark-and-sweep), она сохраняет только те объекты, которые можно отследить с помощью цепочки ссылок, начинающейся с корня программы.

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

Моя оригинальная интерпретация DI

Контейнер DI вводит компоненты с их необходимыми ссылками, и каждый из них хранит ссылку на свою следующую по старшинству команду. У основного класса нет способа добраться до них, поэтому они должны быть собраны с помощью мусора. Контейнер DI НЕ хранит ссылки на свои компоненты

Мой пересмотр DI

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

Ответ №1:

Трудно понять, о чем вы на самом деле спрашиваете:

  • Во всех основных реализациях Java есть сборщики мусора, которые могут собирать мусор с помощью циклических ссылок … или, как их чаще называют, циклы ссылок. Таким образом, нет особой причины избегать циклов ссылок в DI с точки зрения GC.

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

  • Явно объявленные зависимости сообщают платформе DI, что определенные объекты должны быть сконструированы и подключены раньше других. Они не говорят, что к чему должно быть подключено, и существуют ли ссылочные циклы (на уровне Java) или нет.


В ответе @JonnyReeve говорится следующее:

«вообще говоря, ссылка (объект) будет иметь право на сборку мусора только тогда, когда останутся нулевые ссылки (например: вы не сможете получить к ней программный доступ)»

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

Фактически, объект подходит для сбора, если он не достижим. Для Java это определено (JLS 12.6.1) следующим образом:

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

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

Ответ №2:

Сбор мусора в AVM — довольно обширная тема; вместо того, чтобы пытаться перейти к более тонким моментам здесь, я бы посоветовал вам начать со статьи Гранта Скиннера об управлении ресурсами.

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

 public class Client {
    private var myFoo : Foo;

    public function Client() {
        // myFoo will count as a reference; it will not be 'swept'
        myFoo = new Foo();

        // Business as usual.
        myFoo.bar();
    }

    public function destroy() : void {
        // Remove the reference to myFoo, it can now be 'swept' as no other
        // references remain to it and there is no way to access it.
        myFoo = null;

        // This will trip a Null Reference Error because myFoo is no longer a
        // reference to the 'Foo' instance.
        myFoo.bar();
    }
}
  

Комментарии:

1. К сожалению, второй абзац неточен; смотрите Мой обновленный ответ.