From e843f8ed6d567acb67a7bb728c9fbd1e45d48132 Mon Sep 17 00:00:00 2001 From: embeddedt <42941056+embeddedt@users.noreply.github.com> Date: Tue, 25 Apr 2023 19:29:26 -0400 Subject: [PATCH] Clear KubeJS recipe event lists since mods can hold onto the event object --- .../mixin/perf/kubejs/RecipeEventJSMixin.java | 37 +++++++++++++++++++ 1 file changed, 37 insertions(+) diff --git a/src/main/java/org/embeddedt/modernfix/mixin/perf/kubejs/RecipeEventJSMixin.java b/src/main/java/org/embeddedt/modernfix/mixin/perf/kubejs/RecipeEventJSMixin.java index 45fe65cd..a64f754d 100644 --- a/src/main/java/org/embeddedt/modernfix/mixin/perf/kubejs/RecipeEventJSMixin.java +++ b/src/main/java/org/embeddedt/modernfix/mixin/perf/kubejs/RecipeEventJSMixin.java @@ -7,6 +7,7 @@ import dev.latvian.kubejs.recipe.RecipeJS; import dev.latvian.kubejs.recipe.filter.RecipeFilter; import net.minecraft.resources.ResourceLocation; import net.minecraft.world.item.crafting.RecipeManager; +import org.embeddedt.modernfix.ModernFix; import org.embeddedt.modernfix.util.KubeUtil; import org.embeddedt.modernfix.util.ModUtil; import org.spongepowered.asm.mixin.Final; @@ -17,6 +18,9 @@ import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.Inject; import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; +import java.lang.reflect.Field; +import java.lang.reflect.Modifier; +import java.util.Collection; import java.util.List; import java.util.Map; import java.util.function.Consumer; @@ -51,4 +55,37 @@ public class RecipeEventJSMixin { private void clearRecipeRegistry(RecipeManager manager, Map jsonMap, CallbackInfo ci) { KubeUtil.originalRecipesByHash.clear(); } + + /** + * The recipe event object can be leaked in scripts and this wastes 40MB of memory. + */ + @Inject(method = "post", at = @At("RETURN")) + private void clearRecipeLists(CallbackInfo ci) { + ModernFix.LOGGER.info("Clearing KubeJS recipe lists..."); + // Even though we are a mixin class, use reflection so this works across a variety of versions + Field[] fields = RecipeEventJS.class.getDeclaredFields(); + for(Field f : fields) { + try { + if(!Modifier.isStatic(f.getModifiers()) + && (Collection.class.isAssignableFrom(f.getType()) + || Map.class.isAssignableFrom(f.getType())) + ) { + f.setAccessible(true); + Object collection = f.get(this); + int size; + if(collection instanceof Map) { + size = ((Map)collection).size(); + ((Map)collection).clear(); + } else if(collection instanceof Collection) { + size = ((Collection)collection).size(); + ((Collection)collection).clear(); + } else + throw new IllegalStateException(); + ModernFix.LOGGER.debug("Cleared {} with {} entries", f.getName(), size); + } + } catch(RuntimeException | ReflectiveOperationException e) { + ModernFix.LOGGER.debug("Uh oh, couldn't clear field", e); + } + } + } }