Don't keep PlotFlagUpdateHandlers forever

This allows Plots, FlagContainers and its PlotFlagUpdateHandlers being cleaned up by the GC correctly
This commit is contained in:
SirYwell 2021-08-06 10:33:38 +02:00 committed by dordsor21
parent 391476ba26
commit 5ab8d50b86
2 changed files with 32 additions and 5 deletions

View File

@ -85,6 +85,7 @@ import org.checkerframework.checker.nullness.qual.Nullable;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import java.lang.ref.Cleaner;
import java.text.DecimalFormat;
import java.text.SimpleDateFormat;
import java.util.ArrayDeque;
@ -127,6 +128,7 @@ public class Plot {
private static final Logger LOGGER = LogManager.getLogger("PlotSquared/" + Plot.class.getSimpleName());
private static final DecimalFormat FLAG_DECIMAL_FORMAT = new DecimalFormat("0");
private static final MiniMessage MINI_MESSAGE = MiniMessage.builder().build();
private static final Cleaner CLEANER = Cleaner.create();
static Set<Plot> connected_cache;
static Set<CuboidRegion> regions_cache;
@ -256,6 +258,9 @@ public class Plot {
this.temp = temp;
this.flagContainer.setParentContainer(area.getFlagContainer());
PlotSquared.platform().injector().injectMembers(this);
// This is needed, because otherwise the Plot, the FlagContainer and its
// `this::handleUnknown` PlotFlagUpdateHandler won't get cleaned up ever
CLEANER.register(this, this.flagContainer.createCleanupHook());
}
/**

View File

@ -27,14 +27,15 @@ package com.plotsquared.core.plot.flag;
import com.google.common.base.Preconditions;
import com.google.common.collect.ImmutableMap;
import org.checkerframework.checker.nullness.qual.NonNull;
import org.checkerframework.checker.nullness.qual.Nullable;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.checkerframework.checker.nullness.qual.NonNull;
import org.checkerframework.checker.nullness.qual.Nullable;
import org.jetbrains.annotations.ApiStatus;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Locale;
import java.util.Map;
import java.util.Objects;
@ -49,8 +50,9 @@ public class FlagContainer {
private final Map<String, String> unknownFlags = new HashMap<>();
private final Map<Class<?>, PlotFlag<?, ?>> flagMap = new HashMap<>();
private final PlotFlagUpdateHandler plotFlagUpdateHandler;
private final Collection<PlotFlagUpdateHandler> updateSubscribers = new ArrayList<>();
private final Collection<PlotFlagUpdateHandler> updateSubscribers = new HashSet<>();
private FlagContainer parentContainer;
private final PlotFlagUpdateHandler unknownsRef;
/**
* Construct a new flag container with an optional parent container and update handler.
@ -71,7 +73,10 @@ public class FlagContainer {
this.parentContainer = parentContainer;
this.plotFlagUpdateHandler = plotFlagUpdateHandler;
if (!(this instanceof GlobalFlagContainer)) {
GlobalFlagContainer.getInstance().subscribe(this::handleUnknowns);
this.unknownsRef = this::handleUnknowns;
GlobalFlagContainer.getInstance().subscribe(this.unknownsRef);
} else {
this.unknownsRef = null;
}
}
@ -336,6 +341,23 @@ public class FlagContainer {
this.unknownFlags.put(flagName.toLowerCase(Locale.ENGLISH), value);
}
/**
* Creates a cleanup hook that is meant to run once this FlagContainer isn't needed anymore.
* This is to prevent memory leaks. This method is not part of the API.
*
* @return a new Runnable that cleans up once the FlagContainer isn't needed anymore.
*/
@ApiStatus.Internal
public Runnable createCleanupHook() {
return () -> GlobalFlagContainer.getInstance().unsubscribe(unknownsRef);
}
void unsubscribe(final @Nullable PlotFlagUpdateHandler updateHandler) {
if (updateHandler != null) {
this.updateSubscribers.remove(updateHandler);
}
}
public boolean equals(final Object o) {
if (o == this) {
return true;