8168396: Unexpected OOME in GcCauseTest02 and GcTest02
Reviewed-by: tschatzl, dfazunen
--- a/hotspot/test/serviceability/tmtools/jstat/GcCapacityTest.java Mon Nov 21 21:07:45 2016 -0500
+++ b/hotspot/test/serviceability/tmtools/jstat/GcCapacityTest.java Tue Nov 22 16:13:13 2016 +0300
@@ -45,7 +45,7 @@
measurement1.assertConsistency();
// Provoke a gc and verify the changed values
- GcProvoker gcProvoker = GcProvoker.createGcProvoker();
+ GcProvoker gcProvoker = new GcProvoker();
gcProvoker.provokeGc();
JstatGcCapacityResults measurement2 = jstatGcTool.measure();
measurement2.assertConsistency();
--- a/hotspot/test/serviceability/tmtools/jstat/GcCauseTest01.java Mon Nov 21 21:07:45 2016 -0500
+++ b/hotspot/test/serviceability/tmtools/jstat/GcCauseTest01.java Tue Nov 22 16:13:13 2016 +0300
@@ -47,7 +47,7 @@
JstatGcCauseResults measurement1 = jstatGcTool.measure();
measurement1.assertConsistency();
- GcProvoker gcProvoker = GcProvoker.createGcProvoker();
+ GcProvoker gcProvoker = new GcProvoker();
// Provoke GC then run the tool again and get the results asserting that they are reasonable
gcProvoker.provokeGc();
--- a/hotspot/test/serviceability/tmtools/jstat/GcCauseTest02.java Mon Nov 21 21:07:45 2016 -0500
+++ b/hotspot/test/serviceability/tmtools/jstat/GcCauseTest02.java Tue Nov 22 16:13:13 2016 +0300
@@ -27,11 +27,11 @@
* Test scenario:
* tests forces debuggee application eat ~70% of heap and runs jstat.
* jstat should show that ~70% of heap (OC/OU ~= 70%).
+ * @requires vm.opt.ExplicitGCInvokesConcurrent != true
* @modules java.base/jdk.internal.misc
* @library /test/lib
* @library ../share
- * @ignore 8168396
- * @run main/othervm -XX:+UsePerfData -Xmx128M -XX:MaxMetaspaceSize=128M GcCauseTest02
+ * @run main/othervm -XX:+UsePerfData -XX:InitialHeapSize=128M -XX:MaxHeapSize=128M -XX:MaxMetaspaceSize=128M GcCauseTest02
*/
import utils.*;
@@ -48,10 +48,12 @@
JstatGcCauseResults measurement1 = jstatGcTool.measure();
measurement1.assertConsistency();
- GcProvoker gcProvoker = GcProvoker.createGcProvoker();
+ GcProvoker gcProvoker = new GcProvoker();
// Eat metaspace and heap then run the tool again and get the results asserting that they are reasonable
- gcProvoker.eatMetaspaceAndHeap(targetMemoryUsagePercent);
+ gcProvoker.allocateAvailableMetaspaceAndHeap(targetMemoryUsagePercent);
+ // Collect garbage. Also update VM statistics
+ System.gc();
JstatGcCauseResults measurement2 = jstatGcTool.measure();
measurement2.assertConsistency();
--- a/hotspot/test/serviceability/tmtools/jstat/GcNewTest.java Mon Nov 21 21:07:45 2016 -0500
+++ b/hotspot/test/serviceability/tmtools/jstat/GcNewTest.java Tue Nov 22 16:13:13 2016 +0300
@@ -46,7 +46,7 @@
JstatGcNewResults measurement1 = jstatGcTool.measure();
measurement1.assertConsistency();
- GcProvoker gcProvoker = GcProvoker.createGcProvoker();
+ GcProvoker gcProvoker = new GcProvoker();
// Provoke GC and run the tool again
gcProvoker.provokeGc();
--- a/hotspot/test/serviceability/tmtools/jstat/GcTest01.java Mon Nov 21 21:07:45 2016 -0500
+++ b/hotspot/test/serviceability/tmtools/jstat/GcTest01.java Tue Nov 22 16:13:13 2016 +0300
@@ -50,7 +50,7 @@
JstatGcResults measurement1 = jstatGcTool.measure();
measurement1.assertConsistency();
- GcProvoker gcProvoker = GcProvoker.createGcProvoker();
+ GcProvoker gcProvoker = new GcProvoker();
// Provoke GC then run the tool again and get the results
// asserting that they are reasonable
--- a/hotspot/test/serviceability/tmtools/jstat/GcTest02.java Mon Nov 21 21:07:45 2016 -0500
+++ b/hotspot/test/serviceability/tmtools/jstat/GcTest02.java Tue Nov 22 16:13:13 2016 +0300
@@ -28,11 +28,11 @@
* Test scenario:
* tests forces debuggee application eat ~70% of heap and runs jstat.
* jstat should show that ~70% of heap is utilized (OC/OU ~= 70%).
+ * @requires vm.opt.ExplicitGCInvokesConcurrent != true
* @modules java.base/jdk.internal.misc
* @library /test/lib
* @library ../share
- * @ignore 8168396
- * @run main/othervm -XX:+UsePerfData -Xmx128M -XX:MaxMetaspaceSize=128M GcTest02
+ * @run main/othervm -XX:+UsePerfData -XX:InitialHeapSize=128M -XX:MaxHeapSize=128M -XX:MaxMetaspaceSize=128M GcTest02
*/
public class GcTest02 {
@@ -48,10 +48,12 @@
JstatGcResults measurement1 = jstatGcTool.measure();
measurement1.assertConsistency();
- GcProvoker gcProvoker = GcProvoker.createGcProvoker();
+ GcProvoker gcProvoker = new GcProvoker();
// Eat metaspace and heap then run the tool again and get the results asserting that they are reasonable
- gcProvoker.eatMetaspaceAndHeap(targetMemoryUsagePercent);
+ gcProvoker.allocateAvailableMetaspaceAndHeap(targetMemoryUsagePercent);
+ // Collect garbage. Also updates VM statistics
+ System.gc();
JstatGcResults measurement2 = jstatGcTool.measure();
measurement2.assertConsistency();
--- a/hotspot/test/serviceability/tmtools/jstat/utils/GcProvoker.java Mon Nov 21 21:07:45 2016 -0500
+++ b/hotspot/test/serviceability/tmtools/jstat/utils/GcProvoker.java Tue Nov 22 16:13:13 2016 +0300
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2015, 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
@@ -22,34 +22,136 @@
*/
package utils;
+import java.lang.management.ManagementFactory;
+import java.lang.management.MemoryPoolMXBean;
+import java.lang.management.MemoryUsage;
+import java.util.ArrayList;
+import java.util.List;
+
/**
- * This is an interface used to provoke GC and perform other GC-related
+ * This is an class used to provoke GC and perform other GC-related
* procedures
*
*/
-public interface GcProvoker {
+public class GcProvoker{
+
+ // Uses fixed small objects to avoid Humongous objects allocation in G1
+ public static final int MEMORY_CHUNK = 2048;
+ public static final float ALLOCATION_TOLERANCE = 0.05f;
+
+ public static List<Object> allocatedMetaspace;
+ public static List<Object> allocatedMemory;
+
+ private final Runtime runtime;
- /**
- * The default implementation
- *
- * @return the default GC provoker
- */
- public static GcProvoker createGcProvoker() {
- return new GcProvokerImpl();
+ private List<Object> allocateHeap(float targetUsage) {
+ long maxMemory = runtime.maxMemory();
+ List<Object> list = new ArrayList<>();
+ long used = 0;
+ long target = (long) (maxMemory * targetUsage);
+ while (used < target) {
+ try {
+ list.add(new byte[MEMORY_CHUNK]);
+ used += MEMORY_CHUNK;
+ } catch (OutOfMemoryError e) {
+ list = null;
+ throw new RuntimeException("Unexpected OOME '" + e.getMessage() + "' while eating " + targetUsage + " of heap memory.");
+ }
+ }
+ return list;
+ }
+
+ private List<Object> allocateAvailableHeap(float targetUsage) {
+ // Calculates size of free memory after allocation with small tolerance.
+ long minFreeMemory = (long) ((1.0 - (targetUsage + ALLOCATION_TOLERANCE)) * runtime.maxMemory());
+ List<Object> list = new ArrayList<>();
+ do {
+ try {
+ list.add(new byte[MEMORY_CHUNK]);
+ } catch (OutOfMemoryError e) {
+ list = null;
+ throw new RuntimeException("Unexpected OOME '" + e.getMessage() + "' while eating " + targetUsage + " of heap memory.");
+ }
+ } while (runtime.freeMemory() > minFreeMemory);
+ return list;
}
/**
* This method provokes a GC
*/
- public void provokeGc();
+ public void provokeGc() {
+ for (int i = 0; i < 3; i++) {
+ long edenSize = Pools.getEdenCommittedSize();
+ long heapSize = Pools.getHeapCommittedSize();
+ float targetPercent = ((float) edenSize) / (heapSize);
+ if ((targetPercent < 0) || (targetPercent > 1.0)) {
+ throw new RuntimeException("Error in the percent calculation" + " (eden size: " + edenSize + ", heap size: " + heapSize + ", calculated eden percent: " + targetPercent + ")");
+ }
+ allocateHeap(targetPercent);
+ allocateHeap(targetPercent);
+ System.gc();
+ }
+ }
+
+ /**
+ * Allocates heap and metaspace upon exit not less than targetMemoryUsagePercent percents
+ * of heap and metaspace have been consumed.
+ *
+ * @param targetMemoryUsagePercent how many percent of heap and metaspace to
+ * allocate
+ */
+
+ public void allocateMetaspaceAndHeap(float targetMemoryUsagePercent) {
+ // Metaspace should be filled before Java Heap to prevent unexpected OOME
+ // in the Java Heap while filling Metaspace
+ allocatedMetaspace = eatMetaspace(targetMemoryUsagePercent);
+ allocatedMemory = allocateHeap(targetMemoryUsagePercent);
+ }
/**
- * Eats heap and metaspace Upon exit targetMemoryUsagePercent percents of
- * heap and metaspace is have been eaten
+ * Allocates heap and metaspace upon exit targetMemoryUsagePercent percents
+ * of heap and metaspace have been consumed.
*
* @param targetMemoryUsagePercent how many percent of heap and metaspace to
- * eat
+ * allocate
*/
- public void eatMetaspaceAndHeap(float targetMemoryUsagePercent);
+ public void allocateAvailableMetaspaceAndHeap(float targetMemoryUsagePercent) {
+ // Metaspace should be filled before Java Heap to prevent unexpected OOME
+ // in the Java Heap while filling Metaspace
+ allocatedMetaspace = eatMetaspace(targetMemoryUsagePercent);
+ allocatedMemory = allocateAvailableHeap(targetMemoryUsagePercent);
+ }
+
+ private List<Object> eatMetaspace(float targetUsage) {
+ List<Object> list = new ArrayList<>();
+ final String metaspacePoolName = "Metaspace";
+ MemoryPoolMXBean metaspacePool = null;
+ for (MemoryPoolMXBean pool : ManagementFactory.getMemoryPoolMXBeans()) {
+ if (pool.getName().contains(metaspacePoolName)) {
+ metaspacePool = pool;
+ break;
+ }
+ }
+ if (metaspacePool == null) {
+ throw new RuntimeException("MXBean for Metaspace pool wasn't found");
+ }
+ float currentUsage;
+ GeneratedClassProducer gp = new GeneratedClassProducer();
+ do {
+ try {
+ list.add(gp.create(0));
+ } catch (OutOfMemoryError oome) {
+ list = null;
+ throw new RuntimeException("Unexpected OOME '" + oome.getMessage() + "' while eating " + targetUsage + " of Metaspace.");
+ }
+ MemoryUsage memoryUsage = metaspacePool.getUsage();
+ currentUsage = (((float) memoryUsage.getUsed()) / memoryUsage.getMax());
+ } while (currentUsage < targetUsage);
+ return list;
+ }
+
+ public GcProvoker() {
+ runtime = Runtime.getRuntime();
+ }
}