Слушатели Java должны быть удалены? (В целом)

Вы записываете эту функцию

def str_join(*args):
    return ''.join(map(str, args))

Затем вы можете звонить просто, где хотите

str_join('Pine')  # Returns : Pine
str_join('Pine', 'apple')  # Returns : Pineapple
str_join('Pine', 'apple', 3)  # Returns : Pineapple3
29
задан Rex M 26 June 2009 в 04:41
поделиться

12 ответов

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

Вот старый код: (Это - общий шаблон, который я видел на всем протяжении)

class Singleton {
    static Singleton getInstance() {...}
    void addListener(Listener listener) {...}
    void removeListener(Listener listener) {...}
}

class Leaky {
    Leaky() {
        // If the singleton changes the widget we need to know so register a listener
        Singleton singleton = Singleton.getInstance();
        singleton.addListener(new Listener() {
            void handleEvent() {
                doSomething();
            }
        });
    }
    void doSomething() {...}
}

// Elsewhere
while (1) {
    Leaky leaky = new Leaky();
    // ... do stuff
    // leaky falls out of scope
}

Очевидно, это плохо. Многие Leaky создается и никогда не собирается "мусор", потому что слушатели поддерживают их.

Здесь была моя альтернатива, которая зафиксировала мою утечку памяти. Это работает, потому что я только забочусь о слушателе события, в то время как объект существует. Слушатель не должен поддерживать объект.

class Singleton {
    static Singleton getInstance() {...}
    void addListener(Listener listener) {...}
    void removeListener(Listener listener) {...}
}

class NotLeaky {
    private NotLeakyListener listener;
    NotLeaky() {
        // If the singleton changes the widget we need to know so register a listener
        Singleton singleton = Singleton.getInstance();
        listener = new NotLeakyListener(this, singleton);
        singleton.addListener(listener);
    }
    void doSomething() {...}
    protected void finalize() {
        try {
            if (listener != null)
                listener.dispose();
        } finally {
            super.finalize();
        }
    }

    private static class NotLeakyListener implements Listener {
        private WeakReference<NotLeaky> ownerRef;
        private Singleton eventer;
        NotLeakyListener(NotLeaky owner, Singleton e) {
            ownerRef = new WeakReference<NotLeaky>(owner);
            eventer = e;
        }

        void dispose() {
            if (eventer != null) {
                eventer.removeListener(this);
                eventer = null;
            }
        }

        void handleEvent() {
            NotLeaky owner = ownerRef.get();
            if (owner == null) {
                dispose();
            } else {
                owner.doSomething();
            }
        }
    }
}

// Elsewhere
while (1) {
    NotLeaky notleaky = new NotLeaky();
    // ... do stuff
    // notleaky falls out of scope
}
1
ответ дан Jeremy 28 November 2019 в 01:57
поделиться

В ссылочном графике существует цикл. Ссылки B и ссылки B A. Сборщик "мусора" будет обнаруживать циклы и видеть, когда не будет никаких внешних ссылок на A и B, и тогда не соберут обоих.

Попытка использовать финализатор здесь является неправильной. Если B уничтожается, ссылка на A также удаляется.

<час>

оператор: "Предположите, что экземпляр будет совместно использован с некоторыми другими объектами также и переживет экземпляр B". является неправильным. Единственный путь, который произойдет, состоит в том, если слушатель явно удален из куда-нибудь кроме финализатора. Если ссылки на A розданы, который будет подразумевать ссылку на B, и B не будет собран "мусор", потому что существуют внешние ссылки на цикл A-B.

<час>

Дальнейшее обновление:

, Если Вы хотите повредить цикл и не потребовать, чтобы B явно удалил слушателя, можно использовать WeakReference. Что-то вроде этого:

class A {
    void addListener(Listener obj);
    void removeListener(Listener obj);
}

class B {
    private static class InnerListener implements Listener {
        private WeakReference m_owner;
        private WeakReference m_source;

        InnerListener(B owner, A source) {
            m_owner = new WeakReference(owner);
            m_source = new WeakReference(source);
        }

        void listen() {
            // Handling reentrancy on this function left as an excercise.
            B b = (B)m_owner.get();
            if (b == null) {
                if (m_source != null) {
                    A a = (A) m_source.get();
                    if (a != null) {
                        a.removeListener(this);
                        m_source = null;
                    }
                }

                return;
            }
            ...
        }
    }

    private A a;

    B() {
        a = new A();
        a.addListener(new InnerListener(this, a));
    }
}

Мог быть далее обобщен в случае необходимости через несколько классов.

17
ответ дан janm 28 November 2019 в 01:57
поделиться

Если Вы добавили B как слушатель A, и A предназначен для переживания B, завершить запрос B никогда не будут называть, потому что существует экземпляр B в A, таким образом, это никогда не будет собираться "мусор". Вы могли обойти это путем хранения ссылки на B в как WeakReference (который не считают ссылкой во время набора гаража), но было бы лучше явно вычеркнуть из списка B от, когда Вам больше не нужен он.

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

3
ответ дан Aaron 28 November 2019 в 01:57
поделиться

Мое понимание GC - то, что, пока removeListener метод не называют, класс A будет поддерживать ссылку на слушателя и таким образом, это не будет кандидатом на очистку GC (и следовательно завершит, не будет назван).

5
ответ дан workmad3 28 November 2019 в 01:57
поделиться

Необходимо происходить из C++ или некоторого другого языка, где люди реализуют деструкторы. В Java Вы не делаете этого. Вы не переопределяете, завершают, если Вы действительно не знаете то, что Вы делаете. Через 10 лет я никогда не должен был делать это, и я все еще не могу думать о серьезном основании, которое потребовало бы, чтобы я сделал это.

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

3
ответ дан entzik 28 November 2019 в 01:57
поделиться

Желание действительно поддерживает B через анонимный экземпляр.

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

2
ответ дан David Pierre 28 November 2019 в 01:57
поделиться

Хранения ссылка на B через анонимный экземпляр в неявно используемом анонимным типом создается. Это означает, что B не будет освобожден, пока removeListener не называют, и таким образом B завершают, не будет назван.

, Когда A уничтожается, это - анонимная ссылка на B, будет также B уничтоженное открытие путь к B быть освобожденным.

, Но так как B содержит ссылку на это, никогда не происходит. Это походит на вопросы проектирования - если A имеет вызовы слушатель, почему Вам нужен B, чтобы также содержать ссылку на A? Почему бы не передать, который выполнил вызов слушателю при необходимости?

1
ответ дан Asaf R 28 November 2019 в 01:57
поделиться

В Вашей ситуации единственная сборка "мусора" "проблема" - то, что экземпляры B не будут собраны "мусор", в то время как существуют твердые ссылки на общий экземпляр A. Это - то, как сборка "мусора" предположила для работы в Java/.NET. Теперь, если Вам не нравится то, что экземпляры B не собраны "мусор" ранее, необходимо ли спросить себя в том, какая точка Вы хотите, чтобы они прекратили слушать события от A? Как только у Вас есть ответ, Вы будете знать, как зафиксировать дизайн.

2
ответ дан Alexander 28 November 2019 в 01:57
поделиться

Когда B собран "мусор", он должен позволить быть собранным "мусор" также, и поэтому любые ссылки в также. Вы не должны явно удалять ссылки в A.

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

0
ответ дан Steve B. 28 November 2019 в 01:57
поделиться

Как может A переживать B?:

Использование В качестве примера B и A:

public static main(args) {
    B myB = new B();
    myB = null;
}

Поведение я ожидал бы:

GC удалит myB, и в myB экземпляре должен был только сослаться к экземпляр, таким образом, это будет удалено также. Со всеми их присвоенными слушателями?

Сделал Вас, возможно, имейте в виду:

class B {
    private A a;
    B(A a) {
        this.a = a;
        a.addListener(new Listener() {
            void listen() {}
        }
}

С использованием:

public static main(args) {
    A myA = new A();
    B myB = new B(myA);
    myB = null;
}

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

1
ответ дан Andre Bossard 28 November 2019 в 01:57
поделиться

Здание, что @Alexander, сказанный об удалении себя как слушатель:

, Если нет некоторый неопровержимый довод не к, одна вещь, я учился от своих коллег, то, что вместо того, чтобы делать анонимного внутреннего Слушателя, и должным быть сохранить его в переменной, заставляют B реализовать Слушателя, и затем B может удалить себя, когда это должно с a.removeListener(this)

0
ответ дан Paul Tomblin 28 November 2019 в 01:57
поделиться

Желание действительно мешает B собираться "мусор" в Вас, используют стандартные ссылки для хранения слушателей. Кроме того, когда Вы ведете списки слушателей вместо того, чтобы определить новый ArrayList< ListenerType> (); Вы могли сделать что-то как новый ArrayList< WeakReference< ListenerType> > ();

Путем обертывания объекта в weakReference можно помешать ему продлевать жизнь объекта.

Это только работает, конечно, если Вы пишете класс, который содержит слушателей

0
ответ дан pfranza 28 November 2019 в 01:57
поделиться
Другие вопросы по тегам:

Похожие вопросы: