Я только что обнаружил огромную утечку памяти, поэтому я собираюсь назвать код, который создал утечку, как неправильный , а мое исправление, которое не дает утечки, как правильно .
Вот старый код: (Это обычный шаблон, который я видел повсюду)
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
}