Repository: ignite Updated Branches: refs/heads/master 45698810a -> 83b5c0e0a
IGNITE-8347 Test of Memory leaks on restart Ignite node with enabled persistence at ThreadLocal. - Fixes #3889. Signed-off-by: dpavlov <[email protected]> Project: http://git-wip-us.apache.org/repos/asf/ignite/repo Commit: http://git-wip-us.apache.org/repos/asf/ignite/commit/83b5c0e0 Tree: http://git-wip-us.apache.org/repos/asf/ignite/tree/83b5c0e0 Diff: http://git-wip-us.apache.org/repos/asf/ignite/diff/83b5c0e0 Branch: refs/heads/master Commit: 83b5c0e0ae493247dc9ba0db0e1014f6c38821aa Parents: 4569881 Author: tledkov-gridgain <[email protected]> Authored: Thu May 3 16:23:40 2018 +0300 Committer: dpavlov <[email protected]> Committed: Thu May 3 16:23:40 2018 +0300 ---------------------------------------------------------------------- .../apache/ignite/internal/util/GridDebug.java | 69 ++++++++++- .../internal/MemoryLeaksOnRestartNodeTest.java | 118 +++++++++++++++++++ 2 files changed, 186 insertions(+), 1 deletion(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/ignite/blob/83b5c0e0/modules/core/src/main/java/org/apache/ignite/internal/util/GridDebug.java ---------------------------------------------------------------------- diff --git a/modules/core/src/main/java/org/apache/ignite/internal/util/GridDebug.java b/modules/core/src/main/java/org/apache/ignite/internal/util/GridDebug.java index a8af0fb..2fa148e 100644 --- a/modules/core/src/main/java/org/apache/ignite/internal/util/GridDebug.java +++ b/modules/core/src/main/java/org/apache/ignite/internal/util/GridDebug.java @@ -17,6 +17,7 @@ package org.apache.ignite.internal.util; +import com.sun.management.HotSpotDiagnosticMXBean; import java.io.File; import java.io.FileNotFoundException; import java.io.FileOutputStream; @@ -32,6 +33,7 @@ import java.util.Collection; import java.util.Date; import java.util.concurrent.ConcurrentLinkedQueue; import java.util.concurrent.atomic.AtomicReference; +import javax.management.MBeanServer; import org.apache.ignite.internal.util.typedef.X; import org.apache.ignite.internal.util.typedef.internal.U; import org.apache.ignite.lang.IgnitePredicate; @@ -67,7 +69,13 @@ public class GridDebug { /** */ private static boolean allowLog; - /** */ + /** This is the name of the HotSpot Diagnostic MBean */ + private static final String HOTSPOT_BEAN_NAME = "com.sun.management:type=HotSpotDiagnostic"; + + /** field to store the hotspot diagnostic MBean */ + private static volatile HotSpotDiagnosticMXBean hotspotMBean; + + /* */ static { if (LOGS_PATH != null) { File log = new File(new File(LOGS_PATH), new SimpleDateFormat("yyyy-MM-dd_HH-mm-ss-").format(new Date()) + @@ -303,6 +311,65 @@ public class GridDebug { } /** + * Call this method from your application whenever you + * want to dump the heap snapshot into a file. + * + * @param fileName name of the heap dump file + * @param live flag that tells whether to dump + * only the live objects + */ + public static void dumpHeap(String fileName, boolean live) { + // initialize hotspot diagnostic MBean + initHotspotMBean(); + + File f = new File(fileName); + + if (f.exists()) + f.delete(); + + try { + hotspotMBean.dumpHeap(fileName, live); + } + catch (RuntimeException re) { + throw re; + } + catch (Exception exp) { + throw new RuntimeException(exp); + } + } + + /** + * Initialize the hotspot diagnostic MBean field + */ + private static void initHotspotMBean() { + if (hotspotMBean == null) { + synchronized (GridDebug.class) { + if (hotspotMBean == null) + hotspotMBean = getHotspotMBean(); + } + } + } + + /** + * Gets the hotspot diagnostic MBean from the platform MBean server + * @return Diagnostic bean. + */ + private static HotSpotDiagnosticMXBean getHotspotMBean() { + try { + MBeanServer server = ManagementFactory.getPlatformMBeanServer(); + + HotSpotDiagnosticMXBean bean = ManagementFactory.newPlatformMXBeanProxy(server, + HOTSPOT_BEAN_NAME, HotSpotDiagnosticMXBean.class); + + return bean; + } catch (RuntimeException re) { + throw re; + } catch (Exception exp) { + throw new RuntimeException(exp); + } + } + + /** * Debug info queue item. */ @SuppressWarnings({"PublicInnerClass", "PublicField"}) http://git-wip-us.apache.org/repos/asf/ignite/blob/83b5c0e0/modules/core/src/test/java/org/apache/ignite/internal/MemoryLeaksOnRestartNodeTest.java ---------------------------------------------------------------------- diff --git a/modules/core/src/test/java/org/apache/ignite/internal/MemoryLeaksOnRestartNodeTest.java b/modules/core/src/test/java/org/apache/ignite/internal/MemoryLeaksOnRestartNodeTest.java new file mode 100644 index 0000000..56ab091 --- /dev/null +++ b/modules/core/src/test/java/org/apache/ignite/internal/MemoryLeaksOnRestartNodeTest.java @@ -0,0 +1,118 @@ +/* + * 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.ignite.internal; + +import java.io.File; +import org.apache.ignite.IgniteSystemProperties; +import org.apache.ignite.configuration.DataRegionConfiguration; +import org.apache.ignite.configuration.DataStorageConfiguration; +import org.apache.ignite.configuration.IgniteConfiguration; +import org.apache.ignite.internal.util.GridDebug; +import org.apache.ignite.internal.util.typedef.internal.U; +import org.apache.ignite.testframework.junits.common.GridCommonAbstractTest; + +/** + * Tests leaks on node restart with enabled persistence. + */ +public class MemoryLeaksOnRestartNodeTest extends GridCommonAbstractTest { + /** Heap dump file name. */ + private static final String HEAP_DUMP_FILE_NAME = "test.hprof"; + + /** Restarts count. */ + private static final int RESTARTS = 10; + + /** Nodes count. */ + private static final int NODES = 3; + + /** Allow 5Mb leaks on node restart. */ + private static final int ALLOW_LEAK_ON_RESTART_IN_MB = 1; + + /** {@inheritDoc} */ + @Override protected IgniteConfiguration getConfiguration(String igniteInstanceName) throws Exception { + IgniteConfiguration cfg = super.getConfiguration(igniteInstanceName); + + cfg.setDataStorageConfiguration(new DataStorageConfiguration() + .setDefaultDataRegionConfiguration( + new DataRegionConfiguration().setName("mem0").setPersistenceEnabled(false)) + .setDataRegionConfigurations( + new DataRegionConfiguration().setName("disk").setPersistenceEnabled(true), + new DataRegionConfiguration().setName("mem2").setPersistenceEnabled(false))); + + return cfg; + } + + /** {@inheritDoc} */ + @Override protected void beforeTestsStarted() throws Exception { + super.beforeTestsStarted(); + + cleanPersistenceDir(); + } + + /** {@inheritDoc} */ + @Override protected void afterTestsStopped() throws Exception { + stopAllGrids(); + + cleanPersistenceDir(); + + super.afterTestsStopped(); + } + + /** + * @throws Exception On failed. + */ + public void test() throws Exception { + System.setProperty(IgniteSystemProperties.IGNITE_DELAYED_REPLACED_PAGE_WRITE, "false"); + + // Warmup + for (int i = 0; i < RESTARTS / 2; ++i) { + startGrids(NODES); + + U.sleep(500); + + stopAllGrids(); + } + + GridDebug.dumpHeap(HEAP_DUMP_FILE_NAME, true); + + File dumpFile = new File(HEAP_DUMP_FILE_NAME); + + final long size0 = dumpFile.length(); + + // Restarts + for (int i = 0; i < RESTARTS; ++i) { + startGrids(NODES); + + U.sleep(500); + + stopAllGrids(); + + GridDebug.dumpHeap(HEAP_DUMP_FILE_NAME, true); + } + + GridDebug.dumpHeap(HEAP_DUMP_FILE_NAME, true); + + final float leakSize = (float)(dumpFile.length() - size0) / 1024 / 1024 / NODES / RESTARTS; + + assertTrue("Possible leaks detected. The " + leakSize + "M leaks per node restart after " + RESTARTS + + " restarts. See the '" + dumpFile.getAbsolutePath() + "'", + leakSize < ALLOW_LEAK_ON_RESTART_IN_MB); + + // Remove dump if successful. + dumpFile.delete(); + } +} \ No newline at end of file
