Updated Branches: refs/heads/apache-blur-0.2 46ded8a75 -> 10b263767
In preparation for supporting java 7 and the G1 collector, adding a java 7 enhanced version of the GCWatcher. Project: http://git-wip-us.apache.org/repos/asf/incubator-blur/repo Commit: http://git-wip-us.apache.org/repos/asf/incubator-blur/commit/10b26376 Tree: http://git-wip-us.apache.org/repos/asf/incubator-blur/tree/10b26376 Diff: http://git-wip-us.apache.org/repos/asf/incubator-blur/diff/10b26376 Branch: refs/heads/apache-blur-0.2 Commit: 10b2637679e3810d8df9162bb1f9c987b19359b2 Parents: 46ded8a Author: Aaron McCurry <[email protected]> Authored: Mon Oct 7 08:56:23 2013 -0400 Committer: Aaron McCurry <[email protected]> Committed: Mon Oct 7 08:56:23 2013 -0400 ---------------------------------------------------------------------- .../blur/manager/status/QueryStatusManager.java | 4 +- .../org/apache/blur/utils/CreateGarbage.java | 4 +- .../java/org/apache/blur/utils/GCAction.java | 25 ++ .../java/org/apache/blur/utils/GCWatcher.java | 256 ++++--------------- .../org/apache/blur/utils/GCWatcherJdk6.java | 242 ++++++++++++++++++ .../org/apache/blur/utils/GCWatcherJdk7.java | 162 ++++++++++++ 6 files changed, 477 insertions(+), 216 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/incubator-blur/blob/10b26376/blur-core/src/main/java/org/apache/blur/manager/status/QueryStatusManager.java ---------------------------------------------------------------------- diff --git a/blur-core/src/main/java/org/apache/blur/manager/status/QueryStatusManager.java b/blur-core/src/main/java/org/apache/blur/manager/status/QueryStatusManager.java index f9bd583..1cb0765 100644 --- a/blur-core/src/main/java/org/apache/blur/manager/status/QueryStatusManager.java +++ b/blur-core/src/main/java/org/apache/blur/manager/status/QueryStatusManager.java @@ -32,8 +32,8 @@ import org.apache.blur.log.LogFactory; import org.apache.blur.thrift.generated.BlurQuery; import org.apache.blur.thrift.generated.BlurQueryStatus; import org.apache.blur.thrift.generated.QueryState; +import org.apache.blur.utils.GCAction; import org.apache.blur.utils.GCWatcher; -import org.apache.blur.utils.GCWatcher.Action; public class QueryStatusManager { @@ -56,7 +56,7 @@ public class QueryStatusManager { } } }, statusCleanupTimerDelay, statusCleanupTimerDelay); - GCWatcher.registerAction(new Action() { + GCWatcher.registerAction(new GCAction() { @Override public void takeAction() throws Exception { stopAllQueriesForBackPressure(); http://git-wip-us.apache.org/repos/asf/incubator-blur/blob/10b26376/blur-core/src/main/java/org/apache/blur/utils/CreateGarbage.java ---------------------------------------------------------------------- diff --git a/blur-core/src/main/java/org/apache/blur/utils/CreateGarbage.java b/blur-core/src/main/java/org/apache/blur/utils/CreateGarbage.java index df28ad9..95512bd 100644 --- a/blur-core/src/main/java/org/apache/blur/utils/CreateGarbage.java +++ b/blur-core/src/main/java/org/apache/blur/utils/CreateGarbage.java @@ -19,13 +19,11 @@ package org.apache.blur.utils; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; -import org.apache.blur.utils.GCWatcher.Action; - public class CreateGarbage { public static void main(String[] args) throws InterruptedException { final Map<String, String> map = new ConcurrentHashMap<String, String>(); - Action action = new Action() { + GCAction action = new GCAction() { @Override public void takeAction() throws Exception { map.clear(); http://git-wip-us.apache.org/repos/asf/incubator-blur/blob/10b26376/blur-core/src/main/java/org/apache/blur/utils/GCAction.java ---------------------------------------------------------------------- diff --git a/blur-core/src/main/java/org/apache/blur/utils/GCAction.java b/blur-core/src/main/java/org/apache/blur/utils/GCAction.java new file mode 100644 index 0000000..a796784 --- /dev/null +++ b/blur-core/src/main/java/org/apache/blur/utils/GCAction.java @@ -0,0 +1,25 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.blur.utils; + +/** + * The {@link GCAction} interface is used to implement an action that needs to + * be taken to prevent an {@link OutOfMemoryError} exception. + */ +public interface GCAction { + void takeAction() throws Exception; +} http://git-wip-us.apache.org/repos/asf/incubator-blur/blob/10b26376/blur-core/src/main/java/org/apache/blur/utils/GCWatcher.java ---------------------------------------------------------------------- diff --git a/blur-core/src/main/java/org/apache/blur/utils/GCWatcher.java b/blur-core/src/main/java/org/apache/blur/utils/GCWatcher.java index e7b0cb1..c9c3774 100644 --- a/blur-core/src/main/java/org/apache/blur/utils/GCWatcher.java +++ b/blur-core/src/main/java/org/apache/blur/utils/GCWatcher.java @@ -1,5 +1,3 @@ -package org.apache.blur.utils; - /** * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with @@ -16,231 +14,67 @@ package org.apache.blur.utils; * See the License for the specific language governing permissions and * limitations under the License. */ -import java.lang.management.GarbageCollectorMXBean; -import java.lang.management.ManagementFactory; -import java.lang.management.MemoryMXBean; -import java.lang.management.MemoryUsage; -import java.lang.reflect.Field; -import java.lang.reflect.Method; -import java.util.ArrayList; -import java.util.List; -import java.util.Map; -import java.util.Timer; -import java.util.TimerTask; -import java.util.concurrent.TimeUnit; - -import org.apache.blur.log.Log; -import org.apache.blur.log.LogFactory; +package org.apache.blur.utils; -public class GCWatcher extends TimerTask { +import java.util.Properties; - private static final Log LOG = LogFactory.getLog(GCWatcher.class); - private static final String GET_LAST_GC_INFO = "getLastGcInfo"; - private static final String CONCURRENT_MARK_SWEEP = "ConcurrentMarkSweep"; - private static final String CMS_OLD_GEN = "CMS Old Gen"; +public class GCWatcher { - private final Timer _timer; - private final long _period = TimeUnit.MILLISECONDS.toMillis(25); - private GarbageCollectorMXBean _bean; - private final double _ratio; - private Method _method; - private GcInfo _gcInfo; - private long _lastIndex; - private final List<Action> _actions = new ArrayList<Action>(); - private final MemoryMXBean _memoryMXBean; - private static GCWatcher _instance; + private static final String JAVA_VERSION = "java.version"; + private static final String _1_7 = "1.7"; + private static final boolean JDK7; - public interface Action { - void takeAction() throws Exception; - } - - private GCWatcher(double ratio) { - _memoryMXBean = ManagementFactory.getMemoryMXBean(); - List<GarbageCollectorMXBean> garbageCollectorMXBeans = ManagementFactory.getGarbageCollectorMXBeans(); - for (GarbageCollectorMXBean bean : garbageCollectorMXBeans) { - String name = bean.getName(); - if (name.equals(CONCURRENT_MARK_SWEEP)) { - _bean = bean; - try { - _method = _bean.getClass().getDeclaredMethod(GET_LAST_GC_INFO, new Class[] {}); - } catch (SecurityException e) { - throw new RuntimeException(e); - } catch (NoSuchMethodException e) { - throw new RuntimeException(e); - } - _method.setAccessible(true); - } - } - _ratio = ratio; - if (_bean != null) { - _timer = new Timer("gc-watch", true); - _timer.schedule(this, _period, _period); - LOG.info("GCWatcher was setup."); + static { + Properties properties = System.getProperties(); + String javaVersion = properties.getProperty(JAVA_VERSION); + if (javaVersion.startsWith(_1_7)) { + JDK7 = true; } else { - _timer = null; - LOG.warn("GCWatcher was NOT setup."); - } - } - - public static void registerAction(Action action) { - GCWatcher instance = instance(); - if (instance != null) { - synchronized (instance._actions) { - instance._actions.add(action); - } + JDK7 = false; } } - public static synchronized void shutdown() { - GCWatcher instance = instance(); - if (instance != null) { - if (instance._timer != null) { - instance._timer.purge(); - instance._timer.cancel(); - } - } - } - - @Override - public void run() { - try { - Object gcInfo = getGcInfo(_bean); - if (gcInfo != null) { - if (_gcInfo == null) { - _gcInfo = new GcInfo(gcInfo); - } else { - _gcInfo.setGcInfo(gcInfo); - } - if (_lastIndex == _gcInfo.getIndex()) { - return; - } - long startTime = _gcInfo.getStartTime(); - long endTime = _gcInfo.getEndTime(); - Map<String, MemoryUsage> usageBeforeGc = _gcInfo.getUsageBeforeGc(); - Map<String, MemoryUsage> usageAfterGc = _gcInfo.getUsageAfterGc(); - - MemoryUsage before = usageBeforeGc.get(CMS_OLD_GEN); - long usedBefore = before.getUsed(); - - MemoryUsage after = usageAfterGc.get(CMS_OLD_GEN); - long usedAfter = after.getUsed(); - - long totalTime = endTime - startTime; - LOG.info("totalTime spent in GC [{0} ms] collected [{1} bytes]", totalTime, (usedBefore - usedAfter)); - MemoryUsage heapMemoryUsage = _memoryMXBean.getHeapMemoryUsage(); - long max = heapMemoryUsage.getMax(); - long used = heapMemoryUsage.getUsed(); - long upperLimit = (long) (max * _ratio); - if (used > upperLimit) { - LOG.error("----- WARNING !!!! - Heap used [{0}] over limit of [{1}], taking action to avoid an OOM error.", used, - upperLimit); - synchronized (_actions) { - for (Action action : _actions) { - try { - action.takeAction(); - } catch (Exception e) { - LOG.error("Unknown error while trying to take action against an OOM [{0}]", e, action); - } - } - } - } - _lastIndex = _gcInfo.getIndex(); - } - } catch (Exception e) { - e.printStackTrace(); + /** + * Initializes the GCWatcher to watch for any garbage collection that leaves + * more then the given ratio free. If more remains then all the given + * {@link GCAction}s are taken to try and relief the JVM from an + * {@link OutOfMemoryError} exception. + * + * @param ratio + * the ratio of used heap to total heap. + */ + public static void init(double ratio) { + if (JDK7) { + GCWatcherJdk7.init(ratio); + } else { + GCWatcherJdk6.init(ratio); } } - private Object getGcInfo(GarbageCollectorMXBean bean) throws Exception { - return _method.invoke(bean, new Object[] {}); - } - - static class GcInfo { - - private Object _gcInfo; - private final Field _startTimeField; - private final Field _endTimeField; - private final Field _usageAfterGcField; - private final Field _usageBeforeGcField; - private final Field _indexField; - - GcInfo(Object o) throws SecurityException, NoSuchFieldException { - Class<? extends Object> clazz = o.getClass(); - _startTimeField = setup(clazz.getDeclaredField("startTime")); - _endTimeField = setup(clazz.getDeclaredField("endTime")); - _usageAfterGcField = setup(clazz.getDeclaredField("usageAfterGc")); - _usageBeforeGcField = setup(clazz.getDeclaredField("usageBeforeGc")); - _indexField = setup(clazz.getDeclaredField("index")); - setGcInfo(o); - } - - void setGcInfo(Object o) { - _gcInfo = o; - } - - private Field setup(Field field) { - field.setAccessible(true); - return field; - } - - long getStartTime() { - try { - return _startTimeField.getLong(_gcInfo); - } catch (Exception e) { - throw new RuntimeException(e); - } - } - - long getEndTime() { - try { - return _endTimeField.getLong(_gcInfo); - } catch (Exception e) { - throw new RuntimeException(e); - } - } - - long getIndex() { - try { - return _indexField.getLong(_gcInfo); - } catch (Exception e) { - throw new RuntimeException(e); - } - } - - @SuppressWarnings("unchecked") - Map<String, MemoryUsage> getUsageAfterGc() { - try { - return (Map<String, MemoryUsage>) _usageAfterGcField.get(_gcInfo); - } catch (Exception e) { - throw new RuntimeException(e); - } - } - - @SuppressWarnings("unchecked") - Map<String, MemoryUsage> getUsageBeforeGc() { - try { - return (Map<String, MemoryUsage>) _usageBeforeGcField.get(_gcInfo); - } catch (Exception e) { - throw new RuntimeException(e); - } + /** + * Registers an {@link GCAction} to be taken when the JVM is near an + * {@link OutOfMemoryError} condition. + * + * @param action + * the {@link GCAction}. + */ + public static void registerAction(GCAction action) { + if (JDK7) { + GCWatcherJdk7.registerAction(action); + } else { + GCWatcherJdk6.registerAction(action); } - } - public synchronized static void init(double ratio) { - if (_instance == null) { - try { - _instance = new GCWatcher(ratio); - } catch (Exception e) { - LOG.error("GCWatcher had error initializing", e); - } + /** + * Shuts down any internal threads watching the JVM. + */ + public static void shutdown() { + if (JDK7) { + GCWatcherJdk7.shutdown(); } else { - LOG.warn("GCWatcher has already been initialized"); + GCWatcherJdk6.shutdown(); } } - private static GCWatcher instance() { - return _instance; - } - } http://git-wip-us.apache.org/repos/asf/incubator-blur/blob/10b26376/blur-core/src/main/java/org/apache/blur/utils/GCWatcherJdk6.java ---------------------------------------------------------------------- diff --git a/blur-core/src/main/java/org/apache/blur/utils/GCWatcherJdk6.java b/blur-core/src/main/java/org/apache/blur/utils/GCWatcherJdk6.java new file mode 100644 index 0000000..6f374c6 --- /dev/null +++ b/blur-core/src/main/java/org/apache/blur/utils/GCWatcherJdk6.java @@ -0,0 +1,242 @@ +package org.apache.blur.utils; + +/** + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +import java.lang.management.GarbageCollectorMXBean; +import java.lang.management.ManagementFactory; +import java.lang.management.MemoryMXBean; +import java.lang.management.MemoryUsage; +import java.lang.reflect.Field; +import java.lang.reflect.Method; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.Timer; +import java.util.TimerTask; +import java.util.concurrent.TimeUnit; + +import org.apache.blur.log.Log; +import org.apache.blur.log.LogFactory; + +public class GCWatcherJdk6 extends TimerTask { + + private static final Log LOG = LogFactory.getLog(GCWatcherJdk6.class); + private static final String GET_LAST_GC_INFO = "getLastGcInfo"; + private static final String CONCURRENT_MARK_SWEEP = "ConcurrentMarkSweep"; + private static final String CMS_OLD_GEN = "CMS Old Gen"; + + private final Timer _timer; + private final long _period = TimeUnit.MILLISECONDS.toMillis(25); + private GarbageCollectorMXBean _bean; + private final double _ratio; + private Method _method; + private GcInfo _gcInfo; + private long _lastIndex; + private final List<GCAction> _actions = new ArrayList<GCAction>(); + private final MemoryMXBean _memoryMXBean; + private static GCWatcherJdk6 _instance; + + private GCWatcherJdk6(double ratio) { + _memoryMXBean = ManagementFactory.getMemoryMXBean(); + List<GarbageCollectorMXBean> garbageCollectorMXBeans = ManagementFactory.getGarbageCollectorMXBeans(); + for (GarbageCollectorMXBean bean : garbageCollectorMXBeans) { + String name = bean.getName(); + if (name.equals(CONCURRENT_MARK_SWEEP)) { + _bean = bean; + try { + _method = _bean.getClass().getDeclaredMethod(GET_LAST_GC_INFO, new Class[] {}); + } catch (SecurityException e) { + throw new RuntimeException(e); + } catch (NoSuchMethodException e) { + throw new RuntimeException(e); + } + _method.setAccessible(true); + } + } + _ratio = ratio; + if (_bean != null) { + _timer = new Timer("gc-watch", true); + _timer.schedule(this, _period, _period); + LOG.info("GCWatcherJdk6 was setup."); + } else { + _timer = null; + LOG.warn("GCWatcherJdk6 was NOT setup."); + } + } + + public static void registerAction(GCAction action) { + GCWatcherJdk6 instance = instance(); + if (instance != null) { + synchronized (instance._actions) { + instance._actions.add(action); + } + } + } + + public static synchronized void shutdown() { + GCWatcherJdk6 instance = instance(); + if (instance != null) { + if (instance._timer != null) { + instance._timer.purge(); + instance._timer.cancel(); + } + } + } + + @Override + public void run() { + try { + Object gcInfo = getGcInfo(_bean); + if (gcInfo != null) { + if (_gcInfo == null) { + _gcInfo = new GcInfo(gcInfo); + } else { + _gcInfo.setGcInfo(gcInfo); + } + if (_lastIndex == _gcInfo.getIndex()) { + return; + } + long startTime = _gcInfo.getStartTime(); + long endTime = _gcInfo.getEndTime(); + Map<String, MemoryUsage> usageBeforeGc = _gcInfo.getUsageBeforeGc(); + Map<String, MemoryUsage> usageAfterGc = _gcInfo.getUsageAfterGc(); + + MemoryUsage before = usageBeforeGc.get(CMS_OLD_GEN); + long usedBefore = before.getUsed(); + + MemoryUsage after = usageAfterGc.get(CMS_OLD_GEN); + long usedAfter = after.getUsed(); + + long totalTime = endTime - startTime; + LOG.info("totalTime spent in GC [{0} ms] collected [{1} bytes]", totalTime, (usedBefore - usedAfter)); + MemoryUsage heapMemoryUsage = _memoryMXBean.getHeapMemoryUsage(); + long max = heapMemoryUsage.getMax(); + long used = heapMemoryUsage.getUsed(); + long upperLimit = (long) (max * _ratio); + if (used > upperLimit) { + LOG.error("----- WARNING !!!! - Heap used [{0}] over limit of [{1}], taking action to avoid an OOM error.", used, + upperLimit); + synchronized (_actions) { + for (GCAction action : _actions) { + try { + action.takeAction(); + } catch (Exception e) { + LOG.error("Unknown error while trying to take action against an OOM [{0}]", e, action); + } + } + } + } + _lastIndex = _gcInfo.getIndex(); + } + } catch (Exception e) { + e.printStackTrace(); + } + } + + private Object getGcInfo(GarbageCollectorMXBean bean) throws Exception { + return _method.invoke(bean, new Object[] {}); + } + + static class GcInfo { + + private Object _gcInfo; + private final Field _startTimeField; + private final Field _endTimeField; + private final Field _usageAfterGcField; + private final Field _usageBeforeGcField; + private final Field _indexField; + + GcInfo(Object o) throws SecurityException, NoSuchFieldException { + Class<? extends Object> clazz = o.getClass(); + _startTimeField = setup(clazz.getDeclaredField("startTime")); + _endTimeField = setup(clazz.getDeclaredField("endTime")); + _usageAfterGcField = setup(clazz.getDeclaredField("usageAfterGc")); + _usageBeforeGcField = setup(clazz.getDeclaredField("usageBeforeGc")); + _indexField = setup(clazz.getDeclaredField("index")); + setGcInfo(o); + } + + void setGcInfo(Object o) { + _gcInfo = o; + } + + private Field setup(Field field) { + field.setAccessible(true); + return field; + } + + long getStartTime() { + try { + return _startTimeField.getLong(_gcInfo); + } catch (Exception e) { + throw new RuntimeException(e); + } + } + + long getEndTime() { + try { + return _endTimeField.getLong(_gcInfo); + } catch (Exception e) { + throw new RuntimeException(e); + } + } + + long getIndex() { + try { + return _indexField.getLong(_gcInfo); + } catch (Exception e) { + throw new RuntimeException(e); + } + } + + @SuppressWarnings("unchecked") + Map<String, MemoryUsage> getUsageAfterGc() { + try { + return (Map<String, MemoryUsage>) _usageAfterGcField.get(_gcInfo); + } catch (Exception e) { + throw new RuntimeException(e); + } + } + + @SuppressWarnings("unchecked") + Map<String, MemoryUsage> getUsageBeforeGc() { + try { + return (Map<String, MemoryUsage>) _usageBeforeGcField.get(_gcInfo); + } catch (Exception e) { + throw new RuntimeException(e); + } + } + + } + + public synchronized static void init(double ratio) { + if (_instance == null) { + try { + _instance = new GCWatcherJdk6(ratio); + } catch (Exception e) { + LOG.error("GCWatcher had error initializing", e); + } + } else { + LOG.warn("GCWatcher has already been initialized"); + } + } + + private static GCWatcherJdk6 instance() { + return _instance; + } + +} http://git-wip-us.apache.org/repos/asf/incubator-blur/blob/10b26376/blur-core/src/main/java/org/apache/blur/utils/GCWatcherJdk7.java ---------------------------------------------------------------------- diff --git a/blur-core/src/main/java/org/apache/blur/utils/GCWatcherJdk7.java b/blur-core/src/main/java/org/apache/blur/utils/GCWatcherJdk7.java new file mode 100644 index 0000000..b9de5a4 --- /dev/null +++ b/blur-core/src/main/java/org/apache/blur/utils/GCWatcherJdk7.java @@ -0,0 +1,162 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.blur.utils; + +import java.lang.management.GarbageCollectorMXBean; +import java.lang.management.ManagementFactory; +import java.lang.management.MemoryMXBean; +import java.lang.management.MemoryUsage; +import java.lang.reflect.Method; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; + +import javax.management.Notification; +import javax.management.NotificationFilter; +import javax.management.NotificationListener; + +import org.apache.blur.log.Log; +import org.apache.blur.log.LogFactory; + +import com.sun.management.GcInfo; + +public class GCWatcherJdk7 { + + private static final Log LOG = LogFactory.getLog(GCWatcherJdk7.class); + private static final String GET_LAST_GC_INFO = "getLastGcInfo"; + private static GCWatcherJdk7 _instance; + + private final MemoryMXBean _memoryMXBean; + private final double _ratio; + private final List<GCAction> _actions = new ArrayList<GCAction>(); + + private GCWatcherJdk7(double ratio) { + _memoryMXBean = ManagementFactory.getMemoryMXBean(); + List<GarbageCollectorMXBean> garbageCollectorMXBeans = ManagementFactory.getGarbageCollectorMXBeans(); + for (GarbageCollectorMXBean bean : garbageCollectorMXBeans) { + NotificationListener listener = new NotificationListener() { + @Override + public void handleNotification(Notification notification, Object bean) { + GarbageCollectorMXBean garbageCollectorMXBean = (GarbageCollectorMXBean) bean; + GcInfo gcInfo = getGcInfo(garbageCollectorMXBean); + long startTime = gcInfo.getStartTime(); + long endTime = gcInfo.getEndTime(); + Map<String, MemoryUsage> usageBeforeGc = gcInfo.getMemoryUsageBeforeGc(); + Map<String, MemoryUsage> usageAfterGc = gcInfo.getMemoryUsageAfterGc(); + long usedBefore = getTotal(usageBeforeGc); + long usedAfter = getTotal(usageAfterGc); + long totalTime = endTime - startTime; + + LOG.info("GC event totalTime spent in GC [{0} ms] collected [{1} bytes]", totalTime, (usedBefore - usedAfter)); + + MemoryUsage heapMemoryUsage = _memoryMXBean.getHeapMemoryUsage(); + long max = heapMemoryUsage.getMax(); + long used = heapMemoryUsage.getUsed(); + long upperLimit = (long) (max * _ratio); + if (used > upperLimit) { + LOG.error("----- WARNING !!!! - Heap used [{0}] over limit of [{1}], taking action to avoid an OOM error.", + used, upperLimit); + takeAction(); + } + } + + private void takeAction() { + synchronized (_actions) { + for (GCAction action : _actions) { + try { + action.takeAction(); + } catch (Exception e) { + LOG.error("Unknown error while trying to take action against an OOM [{0}]", e, action); + } + } + } + } + + private long getTotal(Map<String, MemoryUsage> memoryUsage) { + long used = 0; + for (Entry<String, MemoryUsage> e : memoryUsage.entrySet()) { + used += e.getValue().getUsed(); + } + return used; + } + }; + + NotificationFilter filter = new NotificationFilter() { + private static final long serialVersionUID = 2971450191223596323L; + + @Override + public boolean isNotificationEnabled(Notification notification) { + return true; + } + }; + + Method method; + try { + method = bean.getClass().getMethod("addNotificationListener", + new Class[] { NotificationListener.class, NotificationFilter.class, Object.class }); + method.setAccessible(true); + method.invoke(bean, new Object[] { listener, filter, bean }); + } catch (Exception e) { + throw new RuntimeException(e); + } + + } + _ratio = ratio; + LOG.info("GCWatcherJdk7 was setup."); + } + + public static synchronized void init(double ratio) { + if (_instance == null) { + try { + _instance = new GCWatcherJdk7(ratio); + } catch (Exception e) { + LOG.error("GCWatcher had error initializing", e); + } + } else { + LOG.warn("GCWatcher has already been initialized"); + } + } + + public static void registerAction(GCAction action) { + GCWatcherJdk7 instance = instance(); + if (instance != null) { + synchronized (instance._actions) { + instance._actions.add(action); + } + } + } + + public static void shutdown() { + + } + + private static GCWatcherJdk7 instance() { + return _instance; + } + + private GcInfo getGcInfo(GarbageCollectorMXBean bean) { + try { + Method method = bean.getClass().getMethod(GET_LAST_GC_INFO, new Class[] {}); + method.setAccessible(true); + return (GcInfo) method.invoke(bean, new Object[] {}); + } catch (Exception e) { + throw new RuntimeException(e); + } + } + +}
