changeset 11201:781e19537e17

8132710: Add tests which check that Humongous objects behave as expected after Young GC 8132712: Add tests which check that Humongous objects behave as expected after Full GC Reviewed-by: jmasa, dfazunen
author kzhaldyb
date Fri, 06 May 2016 17:51:11 +0300
parents 148981ec9f91
children 05c2d0b5fa7d
files test/gc/g1/humongousObjects/objectGraphTest/GC.java test/gc/g1/humongousObjects/objectGraphTest/GCTokens.java test/gc/g1/humongousObjects/objectGraphTest/ObjectGraph.java test/gc/g1/humongousObjects/objectGraphTest/README test/gc/g1/humongousObjects/objectGraphTest/ReferenceInfo.java test/gc/g1/humongousObjects/objectGraphTest/TestObjectGraphAfterGC.java test/gc/g1/humongousObjects/objectGraphTest/TestcaseData.java
diffstat 7 files changed, 964 insertions(+), 0 deletions(-) [+]
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/gc/g1/humongousObjects/objectGraphTest/GC.java	Fri May 06 17:51:11 2016 +0300
@@ -0,0 +1,199 @@
+/*
+ * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ *
+ */
+
+package gc.g1.humongousObjects.objectGraphTest;
+
+import gc.testlibrary.Helpers;
+import jdk.test.lib.Asserts;
+import sun.hotspot.WhiteBox;
+
+import java.util.Arrays;
+import java.util.List;
+import java.util.function.Consumer;
+
+/**
+ * Provides methods to initiate GC of requested type and
+ * checks for states of humongous and non-humongous soft/weak externally
+ * referenced objects after GCs
+ */
+public enum GC {
+
+    YOUNG_GC {
+        @Override
+        public Runnable get() {
+            return WHITE_BOX::youngGC;
+        }
+
+        public Consumer<ReferenceInfo<Object[]>> getChecker() {
+            return getCheckerImpl(false, false, true, false);
+        }
+
+        @Override
+        public List<String> shouldContain() {
+            return Arrays.asList(GCTokens.WB_INITIATED_YOUNG_GC);
+        }
+
+        @Override
+        public List<String> shouldNotContain() {
+            return Arrays.asList(GCTokens.WB_INITIATED_MIXED_GC, GCTokens.FULL_GC, GCTokens.WB_INITIATED_CMC,
+                    GCTokens.CMC, GCTokens.YOUNG_GC);
+        }
+    },
+    FULL_GC {
+        @Override
+        public Runnable get() {
+            return System::gc;
+        }
+
+        public Consumer<ReferenceInfo<Object[]>> getChecker() {
+            return getCheckerImpl(true, false, true, false);
+        }
+
+        @Override
+        public List<String> shouldContain() {
+            return Arrays.asList(GCTokens.FULL_GC);
+        }
+
+        @Override
+        public List<String> shouldNotContain() {
+            return Arrays.asList(GCTokens.WB_INITIATED_YOUNG_GC, GCTokens.WB_INITIATED_MIXED_GC,
+                    GCTokens.WB_INITIATED_CMC, GCTokens.CMC, GCTokens.YOUNG_GC);
+        }
+    },
+
+    FULL_GC_MEMORY_PRESSURE {
+        @Override
+        public Runnable get() {
+            return WHITE_BOX::fullGC;
+        }
+
+        public Consumer<ReferenceInfo<Object[]>> getChecker() {
+            return getCheckerImpl(true, true, true, true);
+        }
+
+        @Override
+        public List<String> shouldContain() {
+            return Arrays.asList(GCTokens.FULL_GC_MEMORY_PRESSURE);
+        }
+
+        @Override
+        public List<String> shouldNotContain() {
+            return Arrays.asList(GCTokens.WB_INITIATED_YOUNG_GC, GCTokens.WB_INITIATED_MIXED_GC,
+                    GCTokens.WB_INITIATED_CMC, GCTokens.CMC, GCTokens.YOUNG_GC, GCTokens.FULL_GC);
+        }
+    };
+
+    protected String getErrorMessage(ReferenceInfo<Object[]> ref, boolean expectedNull, String gcType) {
+        return String.format("Externally effectively %s referenced %shumongous object was%s deleted after %s",
+                (ref.softlyReachable ? "soft" : "weak"), (ref.effectiveHumongous ? "" : "non-"),
+                (expectedNull ? " not" : ""), gcType);
+    }
+
+    protected Consumer<ReferenceInfo<Object[]>> getCaseCheck(boolean expectedNull) {
+        return expectedNull
+                ? r -> Asserts.assertNull(r.reference.get(), getErrorMessage(r, true, name()))
+                : r -> Asserts.assertNotNull(r.reference.get(), getErrorMessage(r, false, name()));
+    }
+
+    protected Consumer<ReferenceInfo<Object[]>> getCheckerImpl(boolean weakH, boolean softH,
+                                                               boolean weakS, boolean softS) {
+        return new Checker(getCaseCheck(weakH), getCaseCheck(softH), getCaseCheck(weakS), getCaseCheck(softS));
+    }
+
+    protected String getGcLogName(String prefix) {
+        return prefix + "_" + name() + ".gc.log";
+    }
+
+    private static final WhiteBox WHITE_BOX = WhiteBox.getWhiteBox();
+
+    /**
+     * @return method to initiate GC
+     */
+    public abstract Runnable get();
+
+    /**
+     * @return checker for objects' states after GC
+     */
+    public abstract Consumer<ReferenceInfo<Object[]>> getChecker();
+
+    /**
+     * @return list of tokens that should be contained in gc log after gc of specified type
+     */
+    public abstract List<String> shouldContain();
+
+    /**
+     * @return list of tokens that should not be contained in gc log after gc of specified type
+     */
+    public abstract List<String> shouldNotContain();
+
+
+    /**
+     * Checks object' state after gc
+     * Contains 4 Consumers which are called depending on humongous/non-humongous and
+     * external weak/soft referenced objects
+     */
+    private static class Checker implements Consumer<ReferenceInfo<Object[]>> {
+        // 4 consumers with checks for (humongous /simple objects)*(weak/soft referenced)
+        final Consumer<ReferenceInfo<Object[]>> weakHumongousCheck;
+        final Consumer<ReferenceInfo<Object[]>> softHumongousCheck;
+        final Consumer<ReferenceInfo<Object[]>> weakSimpleCheck;
+        final Consumer<ReferenceInfo<Object[]>> softSimpleCheck;
+
+        public Checker(Consumer<ReferenceInfo<Object[]>> weakHumongousCheck,
+                       Consumer<ReferenceInfo<Object[]>> softHumongousCheck,
+                       Consumer<ReferenceInfo<Object[]>> weakSimpleCheck,
+                       Consumer<ReferenceInfo<Object[]>> softSimpleCheck) {
+            this.weakHumongousCheck = weakHumongousCheck;
+            this.softHumongousCheck = softHumongousCheck;
+            this.weakSimpleCheck = weakSimpleCheck;
+            this.softSimpleCheck = softSimpleCheck;
+        }
+
+        public void accept(ReferenceInfo<Object[]> ref) {
+
+            System.out.println("reference.get() returned " + ref.reference.get());
+            if (ref.effectiveHumongous && ref.softlyReachable) {
+                System.out.println("soft and humongous");
+                softHumongousCheck.accept(ref);
+            }
+
+            if (ref.effectiveHumongous && !ref.softlyReachable) {
+                System.out.println("weak and humongous");
+                weakHumongousCheck.accept(ref);
+
+            }
+
+            if (!ref.effectiveHumongous && ref.softlyReachable) {
+                System.out.println("soft and non-humongous");
+                softSimpleCheck.accept(ref);
+            }
+
+            if (!ref.effectiveHumongous && !ref.softlyReachable) {
+                System.out.println("weak and non-humongous");
+                weakSimpleCheck.accept(ref);
+            }
+        }
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/gc/g1/humongousObjects/objectGraphTest/GCTokens.java	Fri May 06 17:51:11 2016 +0300
@@ -0,0 +1,42 @@
+/*
+ * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ *
+ */
+
+package gc.g1.humongousObjects.objectGraphTest;
+
+/**
+ * Contains tokens that could appear in gc log
+ */
+public final class GCTokens {
+    // Private c-tor to prevent instantiating
+    private GCTokens() {
+    }
+
+    public static final String WB_INITIATED_YOUNG_GC = "Young (WhiteBox Initiated Young GC)";
+    public static final String WB_INITIATED_MIXED_GC = "Pause Mixed (WhiteBox Initiated Young GC)";
+    public static final String WB_INITIATED_CMC = "WhiteBox Initiated Concurrent Mark";
+    public static final String FULL_GC = "Full (System.gc())";
+    public static final String FULL_GC_MEMORY_PRESSURE = "WhiteBox Initiated Full GC";
+    public static final String CMC = "Concurrent Mark)";
+    public static final String YOUNG_GC = "GC pause (young)";
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/gc/g1/humongousObjects/objectGraphTest/ObjectGraph.java	Fri May 06 17:51:11 2016 +0300
@@ -0,0 +1,150 @@
+/*
+ * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ *
+ */
+
+package gc.g1.humongousObjects.objectGraphTest;
+
+import java.util.ArrayDeque;
+import java.util.Deque;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.function.BiConsumer;
+import java.util.function.Consumer;
+import java.util.function.Predicate;
+
+public class ObjectGraph {
+
+    private ObjectGraph() {
+    }
+
+    public enum ReferenceType {
+        NONE,
+        WEAK,
+        SOFT,
+        STRONG;
+    }
+
+    /**
+     * Performs operation on all nodes that are reachable from initial ones
+     *
+     * @param nodes     initial nodes
+     * @param operation operation
+     */
+    public static void propagateTransitiveProperty(Set<Object[]> nodes, Consumer<Object[]> operation) {
+        Deque<Object[]> roots = new ArrayDeque<>();
+        nodes.stream().forEach(roots::push);
+        ObjectGraph.enumerateAndMark(roots, operation);
+    }
+
+    /**
+     * Connects graph's vertexes with single-directed (vertex -> neighbour) link
+     *
+     * @param vertex    who is connected
+     * @param neighbour connected to whom
+     */
+    private static void connectVertexes(Object[] vertex, Object[] neighbour) {
+
+        // check if vertex array is full
+        if (vertex[vertex.length - 1] != null) {
+            throw new Error("Array is full and no connections could be added");
+        }
+        int i = 0;
+        while (vertex[i] != null) {
+            ++i;
+        }
+        vertex[i] = neighbour;
+    }
+
+
+    /**
+     * Builds object graph using description from list of parsed nodes. Graph uses Object[] as nodes, first n elements
+     * of array are links to connected nodes, others are null. Then runs visitors on generated graph
+     *
+     * @param parsedNodes             list of nodes' description
+     * @param visitors                visitors that will visit each node of generated graph
+     * @param humongousAllocationSize size of humongous node
+     * @param simpleAllocationSize    size of simple (non-humongous) node
+     * @return root reference to generated graph
+     */
+    public static Object[] generateObjectNodes(List<TestcaseData.FinalParsedNode> parsedNodes,
+                                               Map<Predicate<TestcaseData.FinalParsedNode>,
+                                                       BiConsumer<TestcaseData.FinalParsedNode, Object[][]>> visitors,
+                                               int humongousAllocationSize, int simpleAllocationSize) {
+
+        Object[][] objectNodes = new Object[parsedNodes.size()][];
+
+        // Allocating nodes on Object[]
+        for (int i = 0; i < parsedNodes.size(); ++i) {
+            objectNodes[i] = new Object[(parsedNodes.get(i).isHumongous ?
+                    humongousAllocationSize : simpleAllocationSize)];
+        }
+
+        // Connecting nodes on allocated on Object[]
+        for (int i = 0; i < parsedNodes.size(); ++i) {
+            for (int j = 0; j < parsedNodes.get(i).getConnectedTo().size(); ++j) {
+                connectVertexes(objectNodes[i], objectNodes[parsedNodes.get(i).getConnectedTo().get(j)]);
+            }
+        }
+
+        // Calling visitors
+        visitors.entrySet()
+                .stream()
+                .forEach(
+                        entry -> parsedNodes.stream()
+                                .filter(parsedNode -> entry.getKey().test(parsedNode))
+                                .forEach(node -> entry.getValue().accept(node, objectNodes))
+                );
+
+        return objectNodes[0];
+    }
+
+    /**
+     * Enumerates graph starting with provided vertexes. All vertexes that are reachable from the provided ones are
+     * marked
+     *
+     * @param markedParents provided vertexes
+     * @param markVertex    lambda which marks vertexes
+     */
+    public static void enumerateAndMark(Deque<Object[]> markedParents,
+                                        Consumer<Object[]> markVertex) {
+        Map<Object[], Boolean> isVisited = new HashMap<>();
+        while (!markedParents.isEmpty()) {
+            Object[] vertex = markedParents.pop();
+            if (vertex == null || isVisited.containsKey(vertex)) {
+                continue;
+            }
+            isVisited.put(vertex, true);
+            markVertex.accept(vertex);
+
+            for (int i = 0; i < vertex.length; ++i) {
+                if (vertex[i] == null) {
+                    break;
+                }
+                markedParents.add((Object[]) vertex[i]);
+            }
+        }
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/gc/g1/humongousObjects/objectGraphTest/README	Fri May 06 17:51:11 2016 +0300
@@ -0,0 +1,56 @@
+/*
+ * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ *
+ */
+
+The test checks that after different type of GC unreachable objects behave as expected:
+
+1. Young GC - weakly referenced non-humongous objects are collected, other objects are not collected.
+
+2. Full GC - weakly referenced non-humongous and humongous objects are collected, softly referenced non-humongous and
+             humongous objects are not collected.
+
+3. Full GC with memory pressure - weakly and softly referenced non-humongous and humongous objects are collected.
+
+The test gets gc type as a command line argument.
+Then the test allocates object graph in heap (currently testing scenarios are pre-generated and stored in
+TestcaseData.getPregeneratedTestcases()) with TestObjectGraphAfterGC::allocateObjectGraph.
+
+Since we are testing humongous objects we need pretty unusual nodes - arrays of Object.
+We need this since only large enough array could be Humongous object (in fact class with huge amount of fields is
+humongous too but it's for other tests).
+ObjectGraph class generates object graph with Object[] nodes. It also provides a way to collect
+information about each node using "visitor" pattern.
+
+Using visitors we build Set of ReferenceInfo instances which contains the following information:
+reference - external weak/soft reference to graph's node
+graphId and nodeId - graph's and node's ids - we need this for error handling
+softlyReachable - is node effectively referenced by external soft reference. It could be when external
+soft reference or when this node is reachable from node that exteranally referenced by soft reference
+effectiveHumongous - if node behaves effectively humongous.  It could be when node is humongous
+or when this node is reachable from humongous node.
+
+When we leave TestObjectGraphAfterGC::allocateObjectGraph we make graph reachable only with references from Set of
+ReferenceInfo instances.
+
+We run specified gc and check that each instance of ReferenceInfo set behaves as expected.
+Then we check that gc log file contains expected tokens and doesn't contain tokens that it should not contain.
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/gc/g1/humongousObjects/objectGraphTest/ReferenceInfo.java	Fri May 06 17:51:11 2016 +0300
@@ -0,0 +1,63 @@
+/*
+ * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ *
+ */
+
+package gc.g1.humongousObjects.objectGraphTest;
+
+import java.lang.ref.Reference;
+
+/**
+ * Immutable structure that holds the following information about graph's node
+ * reference - weak/soft reference to graph's node
+ * graphId and nodeId - graph's and node's ids - we need this for error handling
+ * softlyReachable - is node effectively referenced by external soft reference. It could be when external
+ * soft reference or when this node is reachable from node that externally referenced by soft reference
+ * effectiveHumongous - if node behaves effectively humongous.  It could be when node is humongous
+ * or when this node is reachable from humongous node.
+ *
+ * @param <T> - actual type of node
+ */
+public class ReferenceInfo<T> {
+    public final Reference<T> reference;
+    public final String graphId;
+    public final String nodeId;
+    public final boolean softlyReachable;
+    public final boolean effectiveHumongous;
+
+    public ReferenceInfo(Reference<T> reference, String graphId, String nodeId, boolean softlyReachable,
+                         boolean effectiveHumongous) {
+        this.reference = reference;
+        this.graphId = graphId;
+        this.nodeId = nodeId;
+        this.softlyReachable = softlyReachable;
+        this.effectiveHumongous = effectiveHumongous;
+    }
+
+    @Override
+    public String toString() {
+        return String.format("Node %s is effectively %shumongous and effectively %ssoft referenced\n"
+                        + "\tReference type is %s and it points to %s", nodeId,
+                (effectiveHumongous ? "" : "non-"), (softlyReachable ? "" : "non-"),
+                reference.getClass().getSimpleName(), reference.get());
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/gc/g1/humongousObjects/objectGraphTest/TestObjectGraphAfterGC.java	Fri May 06 17:51:11 2016 +0300
@@ -0,0 +1,247 @@
+/*
+ * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ *
+ */
+
+package gc.g1.humongousObjects.objectGraphTest;
+
+import jdk.test.lib.OutputAnalyzer;
+import sun.hotspot.WhiteBox;
+
+import java.io.File;
+import java.io.IOException;
+import java.lang.ref.Reference;
+import java.lang.ref.ReferenceQueue;
+import java.lang.ref.SoftReference;
+import java.lang.ref.WeakReference;
+import java.nio.file.Files;
+import java.util.Map;
+import java.util.HashMap;
+import java.util.List;
+import java.util.HashSet;
+import java.util.Set;
+import java.util.function.BiConsumer;
+import java.util.function.Consumer;
+import java.util.function.Predicate;
+import java.util.stream.Collectors;
+
+
+/**
+ * @test TestObjectGraphAfterGC
+ * @summary Checks that objects' graph behave as expected after gc
+ * @requires vm.gc=="G1" | vm.gc=="null"
+ * @requires vm.opt.ExplicitGCInvokesConcurrent != true
+ * @library /testlibrary /test/lib /
+ * @modules java.management java.base/jdk.internal.misc
+ * @build sun.hotspot.WhiteBox
+ *        gc.testlibrary.Helpers
+ *        gc.g1.humongousObjects.objectGraphTest.GCTokens
+ *        gc.g1.humongousObjects.objectGraphTest.ReferenceInfo
+ *        gc.g1.humongousObjects.objectGraphTest.GC
+ *        gc.g1.humongousObjects.objectGraphTest.ObjectGraph
+ *        gc.g1.humongousObjects.objectGraphTest.TestObjectGraphAfterGC
+ * @run driver ClassFileInstaller sun.hotspot.WhiteBox
+ *                                sun.hotspot.WhiteBox$WhiteBoxPermission
+ *
+ * @run main/othervm -Xms200M -XX:+UseG1GC -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -Xbootclasspath/a:.
+ * -XX:G1HeapRegionSize=1M -Xlog:gc*=debug:file=TestObjectGraphAfterGC_YOUNG_GC.gc.log
+ * gc.g1.humongousObjects.objectGraphTest.TestObjectGraphAfterGC YOUNG_GC
+ *
+ * @run main/othervm -Xms200M -XX:+UseG1GC -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -Xbootclasspath/a:.
+ * -XX:G1HeapRegionSize=1M -Xlog:gc=info:file=TestObjectGraphAfterGC_FULL_GC.gc.log
+ * gc.g1.humongousObjects.objectGraphTest.TestObjectGraphAfterGC FULL_GC
+ *
+ * @run main/othervm -Xms200M -XX:+UseG1GC -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -Xbootclasspath/a:.
+ * -XX:G1HeapRegionSize=1M -Xlog:gc=info:file=TestObjectGraphAfterGC_FULL_GC_MEMORY_PRESSURE.gc.log
+ * gc.g1.humongousObjects.objectGraphTest.TestObjectGraphAfterGC FULL_GC_MEMORY_PRESSURE
+ *
+ */
+
+/**
+ * Checks that objects' graph behave as expected after gc
+ * See README file for detailed info on test's logic
+ */
+public class TestObjectGraphAfterGC {
+
+    private static final int simpleAllocationSize = 1024;
+
+    /**
+     * Entry point
+     *
+     * @param args - first argument - gc name
+     */
+    public static void main(String[] args) {
+
+        if (args.length < 1) {
+            throw new Error("Expected gc name wasn't provided as command line argument");
+        }
+
+        GC gcType = GC.valueOf(args[0].toUpperCase());
+
+        System.out.println("Testing " + gcType.name());
+
+        TestcaseData.getPregeneratedTestcases().stream().forEach(testcase -> {
+            System.out.println("Testcase: " + testcase);
+
+            try {
+                TestObjectGraphAfterGC.doTesting(testcase, gcType.get(), gcType.getChecker(),
+                        gcType.getGcLogName(TestObjectGraphAfterGC.class.getSimpleName()), gcType.shouldContain(),
+                        gcType.shouldNotContain());
+            } catch (IOException e) {
+                throw new Error("Problems trying to find or open " + TestObjectGraphAfterGC.class.getSimpleName()
+                        + ".gc.log", e);
+            }
+            System.out.println(" Passed");
+        });
+    }
+
+    /**
+     * Implements testing with 3 methods - allocateObjectGraph, checkResults and checkGCLog
+     *
+     * @param testcaseData     testcase in the following notation:
+     *                         H - humongous node
+     *                         S - non-humongous node
+     *                         s - external soft reference
+     *                         w - external weak reference
+     *                         Hs->Sw - 1st node is humongous, externally soft referenced and strong references to
+     *                         non-humongous node 2 which is externally weak referenced
+     *                         H->1 - humongous node connects to the first node of chain
+     * @param doGC             method that initiates gc
+     * @param checker          consumer that checks node's state after gc and throws Error if it's wrong
+     * @param gcLogName        name of gc log
+     * @param shouldContain    list of tokens that should be contained in gc log
+     * @param shouldNotContain list of tokens that should not be contained in gc log
+     * @throws IOException if there are some issues with gc log
+     */
+    private static void doTesting(String testcaseData, Runnable doGC, Consumer<ReferenceInfo<Object[]>> checker,
+                                  String gcLogName, List<String> shouldContain, List<String> shouldNotContain)
+            throws IOException {
+        Set<ReferenceInfo<Object[]>> nodeData = allocateObjectGraph(testcaseData);
+        doGC.run();
+        checkResults(nodeData, checker);
+        checkGCLog(gcLogName, shouldContain, shouldNotContain);
+    }
+
+    /**
+     * Allocates a number of objects of humongous and regular size and links then with strong references.
+     * How many objects to create, their size and links between them is encoded in the given parameters.
+     * As the result an object graph will be created.
+     * For the testing purpose for each created object (a graph node) an extra ReferenceInfo object will be created.
+     * The ReferenceInfo instances will contain either weak or soft reference to the graph node.
+     *
+     * @param testcaseData testcase in the
+     *                     <p>
+     *                     H - humongous node
+     *                     S - non-humongous node
+     *                     s - external soft reference
+     *                     w - external weak reference
+     *                     Hs->Sw - 1st node is humongous, externally soft referenced and strong references to
+     *                     non-humongous node 2 which is externally weak referenced
+     *                     H->1 - humongous node connects to the first node of chain
+     * @return set of ReferenceInfo objects containing weak/soft reference to the graph node and other data on how
+     * objects should behave after gc
+     */
+    private static Set<ReferenceInfo<Object[]>> allocateObjectGraph(String testcaseData) {
+        Map<Object[], String> nodeIds = new HashMap<>();
+        Set<Object[]> humongousNodes = new HashSet<>();
+        Set<Object[]> externalSoftReferenced = new HashSet<>();
+        Set<Object[]> externalWeakReferenced = new HashSet<>();
+
+        Map<Predicate<TestcaseData.FinalParsedNode>, BiConsumer<TestcaseData.FinalParsedNode, Object[][]>> visitors
+                = new HashMap<>();
+
+        visitors.put((parsedNode -> true),
+                (parsedNode, objects) -> nodeIds.put(objects[Integer.valueOf(parsedNode.id)], parsedNode.id)
+        );
+
+        visitors.put((parsedNode -> parsedNode.isHumongous),
+                (parsedNode, objects) -> humongousNodes.add(objects[Integer.valueOf(parsedNode.id)])
+        );
+
+        visitors.put(parsedNode -> parsedNode.getReferencesTypes().stream().
+                        anyMatch(referenceType -> referenceType == ObjectGraph.ReferenceType.SOFT),
+                (parsedNode, objects) -> externalSoftReferenced.add(objects[Integer.valueOf(parsedNode.id)])
+        );
+
+        visitors.put(parsedNode -> parsedNode.getReferencesTypes().stream().
+                        anyMatch(referenceType -> referenceType == ObjectGraph.ReferenceType.WEAK),
+                (parsedNode, objects) -> externalWeakReferenced.add(objects[Integer.valueOf(parsedNode.id)])
+        );
+
+        List<TestcaseData.FinalParsedNode> internalParsedNodes = TestcaseData.parse(testcaseData);
+
+        Object[] root = ObjectGraph.generateObjectNodes(internalParsedNodes, visitors,
+                WhiteBox.getWhiteBox().g1RegionSize(), simpleAllocationSize);
+
+        ObjectGraph.propagateTransitiveProperty(humongousNodes, humongousNodes::add);
+        Set<Object[]> effectiveSoftReferenced = new HashSet<>();
+        ObjectGraph.propagateTransitiveProperty(externalSoftReferenced, effectiveSoftReferenced::add);
+
+        // Create external references
+        ReferenceQueue<Object[]> referenceQueue = new ReferenceQueue<>();
+        Set<Reference<Object[]>> externalRefs = new HashSet<>();
+
+        externalWeakReferenced.stream()
+                .forEach(objects -> externalRefs.add(new WeakReference<>(objects, referenceQueue)));
+        externalSoftReferenced.stream()
+                .forEach(objects -> externalRefs.add(new SoftReference<>(objects, referenceQueue)));
+
+        return externalRefs.stream()
+                .map(ref -> new ReferenceInfo<>(ref, testcaseData, nodeIds.get(ref.get()),
+                        effectiveSoftReferenced.contains(ref.get()), humongousNodes.contains(ref.get())))
+                .collect(Collectors.toSet());
+
+    }
+
+    /**
+     * Checks that object' state after gc is as expected
+     *
+     * @param nodeData array with information about nodes
+     * @param checker  consumer that checks node's state after gc and throws Error if it's wrong
+     */
+    private static void checkResults(Set<ReferenceInfo<Object[]>> nodeData, Consumer<ReferenceInfo<Object[]>> checker) {
+        nodeData.stream().forEach(checker::accept);
+    }
+
+    /**
+     * Checks that gc log contains what we expected and does not contain what we didn't expect
+     *
+     * @param gcLogName        gc log name
+     * @param shouldContain    list of tokens that should be contained in gc log
+     * @param shouldNotContain list of tokens that should not be contained in gc log
+     * @throws IOException if there are some issues with gc log
+     */
+    private static void checkGCLog(String gcLogName, List<String> shouldContain, List<String> shouldNotContain)
+            throws IOException {
+
+        if (gcLogName == null) {
+            return;
+        }
+        String gcLog = new String(Files.readAllBytes(new File(gcLogName).toPath()));
+
+        OutputAnalyzer outputAnalyzer = new OutputAnalyzer(gcLog, "");
+
+        shouldContain.stream().forEach(outputAnalyzer::shouldContain);
+        shouldNotContain.stream().forEach(outputAnalyzer::shouldNotContain);
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/gc/g1/humongousObjects/objectGraphTest/TestcaseData.java	Fri May 06 17:51:11 2016 +0300
@@ -0,0 +1,207 @@
+/*
+ * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ *
+ */
+
+package gc.g1.humongousObjects.objectGraphTest;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+import java.util.stream.Collectors;
+
+public final class TestcaseData {
+    /**
+     * Temporary node description used during parsing
+     */
+    private static class InternalParsedNode {
+        public String id;
+        public final ArrayList<Integer> connectedTo = new ArrayList<>();
+        public final ArrayList<Integer> connectedFrom = new ArrayList<>();
+        public final List<ObjectGraph.ReferenceType> referencesTypes = new ArrayList<>();
+        public boolean isHumongous;
+    }
+
+    /**
+     * Immutable node description.
+     * Contains:
+     * Node id
+     * Humongous flag
+     * List of external references' types
+     * List of nodes connected to
+     */
+    public static class FinalParsedNode {
+        public final String id;
+        public final boolean isHumongous;
+        private final List<ObjectGraph.ReferenceType> referencesTypes;
+        private final ArrayList<Integer> connectedTo;
+
+
+        public FinalParsedNode(InternalParsedNode internalParsedNode) {
+            referencesTypes = internalParsedNode.referencesTypes;
+            connectedTo = internalParsedNode.connectedTo;
+            id = internalParsedNode.id;
+            isHumongous = internalParsedNode.isHumongous;
+        }
+
+        public List<ObjectGraph.ReferenceType> getReferencesTypes() {
+            return Collections.unmodifiableList(referencesTypes);
+        }
+
+        public List<Integer> getConnectedTo() {
+            return Collections.unmodifiableList(connectedTo);
+        }
+    }
+
+    /**
+     * @param testcaseDesc testcase in the following notation:
+     *                     H - humongous node
+     *                     S - non-humongous node
+     *                     s - external soft reference
+     *                     w - external weak reference
+     *                     Hs->Sw - 1st node is humongous, externally soft referenced and strong references to non-humongous node 2 which is
+     *                     externally weak referenced
+     *                     H->1 - humongous node connects to the first node of chain
+     * @return list of nodes description in FinalParsedNode structure
+     */
+    public static List<FinalParsedNode> parse(String testcaseDesc) {
+        String[] nodes = testcaseDesc.split("-");
+        List<InternalParsedNode> internalParsedNodeList = new ArrayList<>();
+
+        for (int i = 0; i < nodes.length; ++i) {
+            String node = nodes[i];
+            InternalParsedNode nd;
+            if (node.contains("1")) {
+                nd = internalParsedNodeList.get(0);
+
+            } else {
+                nd = new InternalParsedNode();
+                internalParsedNodeList.add(nd);
+                nd.id = String.valueOf(i);
+            }
+
+            if (node.startsWith(">")) {
+                nd.connectedFrom.add(i - 1);
+            }
+            if (node.endsWith("<")) {
+                nd.connectedFrom.add(i + 1);
+            }
+            if (node.contains("w")) {
+                nd.referencesTypes.add(ObjectGraph.ReferenceType.WEAK);
+            }
+
+            if (node.contains("s")) {
+                nd.referencesTypes.add(ObjectGraph.ReferenceType.SOFT);
+            }
+            if (node.contains("H")) {
+                nd.isHumongous = true;
+            }
+
+            if (node.contains("S")) {
+                nd.isHumongous = false;
+            }
+        }
+
+        // we have connectedFrom but we need to get connectedTo
+        for (int i = 0; i < internalParsedNodeList.size(); ++i) {
+            for (Integer reference : internalParsedNodeList.get(i).connectedFrom) {
+                internalParsedNodeList.get(reference).connectedTo.add(i);
+            }
+        }
+
+        List<FinalParsedNode> finalParsedNodes = internalParsedNodeList.stream().map(FinalParsedNode::new)
+                .collect(Collectors.toList());
+
+        return finalParsedNodes;
+    }
+
+    /**
+     * @return List of pregenerated testing cases
+     */
+    public static List<String> getPregeneratedTestcases() {
+        return Arrays.asList(
+                "Hw",
+                "Sw",
+                "Sw->Hw",
+                "Hw->Sw",
+                "Sw<->Hw",
+                "Sw<->Sw",
+                "Hw->Sw->Sw",
+                "Hw->Sw->Sw",
+                "Sw->Hw->Sw",
+                "Hw->Sw->Sw->1",
+                "Sw->Hw->Sw->1",
+                "Sw->Hw->Hw->1",
+                "Sw<->Hw<->Hw->1",
+                "Sw<->Hw<->Sw->1",
+                "Sw->Hw<->Sw",
+                "Hs",
+                "Ss",
+                "Ss->Hs",
+                "Hs->Ss",
+                "Ss<->Hs",
+                "Ss<->Ss",
+                "Hs->Ss->Ss",
+                "Hs->Ss->Ss",
+                "Ss->Hs->Ss",
+                "Hs->Ss->Ss->1",
+                "Ss->Hs->Ss->1",
+                "Ss->Hs->Hs->1",
+                "Ss<->Hs<->Hs->1",
+                "Ss<->Hs<->Ss->1",
+                "Ss->Hs<->Ss",
+                "Ss->Hw",
+                "Sw->Hs",
+                "Hs->Sw",
+                "Hw->Ss",
+                "Ss<->Hw",
+                "Sw<->Hs",
+                "Ss<->Sw",
+                "Sw<->Ss",
+                "Hs->Sw->Sw",
+                "Hw->Ss->Sw",
+                "Hw->Sw->Ss",
+                "Ss->Hw->Sw",
+                "Sw->Hs->Sw",
+                "Sw->Hw->Ss",
+                "Hs->Sw->Sw->1",
+                "Hw->Ss->Sw->1",
+                "Hw->Sw->Ss->1",
+                "Ss->Hw->Sw->1",
+                "Ss->Hs->Sw->1",
+                "Sw->Hw->Ss->1",
+                "Ss->Hw->Hw->1",
+                "Sw->Hs->Hw->1",
+                "Sw->Hw->Hs->1",
+                "Ss<->Hw<->Hw->1",
+                "Sw<->Hs<->Hw->1",
+                "Sw<->Hw<->Hs->1",
+                "Ss<->Hw<->Sw->1",
+                "Sw<->Hs<->Sw->1",
+                "Sw<->Hw<->Ss->1",
+                "Ss->Hw<->Sw",
+                "Sw->Hs<->Sw",
+                "Sw->Hw<->Ss"
+        );
+    }
+}