From b8b39b47850cfc72751a0e862f69c8d9875fc390 Mon Sep 17 00:00:00 2001 From: "Kristian S. Stangeland" Date: Sun, 10 Feb 2013 15:23:12 +0100 Subject: [PATCH] Set a maximum perm gen space usage for our background compiler. --- ItemDisguise/.classpath | 6 - .../reflect/compiler/BackgroundCompiler.java | 110 +++++++++++++++++- .../reflect/compiler/StructureCompiler.java | 2 +- 3 files changed, 108 insertions(+), 10 deletions(-) diff --git a/ItemDisguise/.classpath b/ItemDisguise/.classpath index 2bda6dc7..71e70473 100644 --- a/ItemDisguise/.classpath +++ b/ItemDisguise/.classpath @@ -7,12 +7,6 @@ - - - - - - diff --git a/ProtocolLib/src/main/java/com/comphenix/protocol/reflect/compiler/BackgroundCompiler.java b/ProtocolLib/src/main/java/com/comphenix/protocol/reflect/compiler/BackgroundCompiler.java index 4359e080..836a45d1 100644 --- a/ProtocolLib/src/main/java/com/comphenix/protocol/reflect/compiler/BackgroundCompiler.java +++ b/ProtocolLib/src/main/java/com/comphenix/protocol/reflect/compiler/BackgroundCompiler.java @@ -17,6 +17,10 @@ package com.comphenix.protocol.reflect.compiler; +import java.lang.management.ManagementFactory; +import java.lang.management.MemoryPoolMXBean; +import java.lang.management.MemoryUsage; +import java.util.List; import java.util.Map; import java.util.concurrent.Callable; import java.util.concurrent.ExecutorService; @@ -29,6 +33,9 @@ import javax.annotation.Nullable; import com.comphenix.protocol.error.ErrorReporter; import com.comphenix.protocol.reflect.StructureModifier; +import com.comphenix.protocol.reflect.compiler.StructureCompiler.StructureKey; +import com.google.common.collect.Lists; +import com.google.common.collect.Maps; import com.google.common.util.concurrent.ThreadFactoryBuilder; /** @@ -48,15 +55,26 @@ public class BackgroundCompiler { // How long to wait for a shutdown public static final int SHUTDOWN_DELAY_MS = 2000; + /** + * The default fraction of perm gen space after which the background compiler will be disabled. + */ + public static final double DEFAULT_DISABLE_AT_PERM_GEN = 0.65; + // The single background compiler we're using private static BackgroundCompiler backgroundCompiler; + // Classes we're currently compiling + private Map>> listeners = Maps.newHashMap(); + private Object listenerLock = new Object(); + private StructureCompiler compiler; private boolean enabled; private boolean shuttingDown; private ExecutorService executor; private ErrorReporter reporter; + + private double disablePermGenFraction = DEFAULT_DISABLE_AT_PERM_GEN; /** * Retrieves the current background compiler. @@ -139,26 +157,61 @@ public class BackgroundCompiler { * @param uncompiled - structure modifier to compile. * @param listener - listener responsible for responding to the compilation. */ + @SuppressWarnings({"rawtypes", "unchecked"}) public void scheduleCompilation(final StructureModifier uncompiled, final CompileListener listener) { - // Only schedule if we're enabled if (enabled && !shuttingDown) { + // Check perm gen + if (getPermGenUsage() > disablePermGenFraction) + return; // Don't try to schedule anything if (executor == null || executor.isShutdown()) return; + + // Use to look up structure modifiers + final StructureKey key = new StructureKey(uncompiled); + + // Allow others to listen in too + synchronized (listenerLock) { + List list = listeners.get(key); + + if (!listeners.containsKey(key)) { + listeners.put(key, (List) Lists.newArrayList(listener)); + } else { + // We're currently compiling + list.add(listener); + return; + } + } // Create the worker that will compile our modifier Callable worker = new Callable() { @Override public Object call() throws Exception { StructureModifier modifier = uncompiled; + List list = null; // Do our compilation try { modifier = compiler.compile(modifier); - listener.onCompiled(modifier); - + + synchronized (listenerLock) { + list = listeners.get(key); + } + + // Only execute the listeners if there is a list + if (list != null) { + for (Object compileListener : list) { + ((CompileListener) compileListener).onCompiled(modifier); + } + + // Remove it when we're done + synchronized (listenerLock) { + list = listeners.remove(key); + } + } + } catch (Throwable e) { // Disable future compilations! setEnabled(false); @@ -205,6 +258,41 @@ public class BackgroundCompiler { } } + /** + * Add a compile listener if we are still waiting for the structure modifier to be compiled. + * @param uncompiled - the structure modifier that may get compiled. + * @param listener - the listener to invoke in that case. + */ + @SuppressWarnings("unchecked") + public void addListener(final StructureModifier uncompiled, final CompileListener listener) { + synchronized (listenerLock) { + StructureKey key = new StructureKey(uncompiled); + + @SuppressWarnings("rawtypes") + List list = listeners.get(key); + + if (list != null) { + list.add(listener); + } + } + } + + /** + * Retrieve the current usage of the Perm Gen space in percentage. + * @return Usage of the perm gen space. + */ + private double getPermGenUsage() { + for (MemoryPoolMXBean item : ManagementFactory.getMemoryPoolMXBeans()) { + if (item.getName().contains("Perm Gen")) { + MemoryUsage usage = item.getUsage(); + return usage.getUsed() / (double) usage.getCommitted(); + } + } + + // Unknown + return 0; + } + /** * Clean up after ourselves using the default timeout. */ @@ -246,6 +334,22 @@ public class BackgroundCompiler { this.enabled = enabled; } + /** + * Retrieve the fraction of perm gen space used after which the background compiler will be disabled. + * @return The fraction after which the background compiler is disabled. + */ + public double getDisablePermGenFraction() { + return disablePermGenFraction; + } + + /** + * Set the fraction of perm gen space used after which the background compiler will be disabled. + * @param fraction - the maximum use of perm gen space. + */ + public void setDisablePermGenFraction(double fraction) { + this.disablePermGenFraction = fraction; + } + /** * Retrieve the current structure compiler. * @return Current structure compiler. diff --git a/ProtocolLib/src/main/java/com/comphenix/protocol/reflect/compiler/StructureCompiler.java b/ProtocolLib/src/main/java/com/comphenix/protocol/reflect/compiler/StructureCompiler.java index 13d90652..4a4eedd3 100644 --- a/ProtocolLib/src/main/java/com/comphenix/protocol/reflect/compiler/StructureCompiler.java +++ b/ProtocolLib/src/main/java/com/comphenix/protocol/reflect/compiler/StructureCompiler.java @@ -95,7 +95,7 @@ public final class StructureCompiler { // Used to store generated classes of different types @SuppressWarnings("rawtypes") - private static class StructureKey { + static class StructureKey { private Class targetType; private Class fieldType;