Path: blob/master/test/hotspot/jtreg/vmTestbase/vm/gc/concurrent/Concurrent.java
41155 views
/*1* Copyright (c) 2010, 2020, Oracle and/or its affiliates. All rights reserved.2* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.3*4* This code is free software; you can redistribute it and/or modify it5* under the terms of the GNU General Public License version 2 only, as6* published by the Free Software Foundation.7*8* This code is distributed in the hope that it will be useful, but WITHOUT9* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or10* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License11* version 2 for more details (a copy is included in the LICENSE file that12* accompanied this code).13*14* You should have received a copy of the GNU General Public License version15* 2 along with this work; if not, write to the Free Software Foundation,16* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.17*18* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA19* or visit www.oracle.com if you need additional information or have any20* questions.21*/22package vm.gc.concurrent;2324import java.lang.management.ManagementFactory;25import java.lang.management.MemoryMXBean;26import java.lang.management.MemoryUsage;27import java.util.concurrent.atomic.AtomicInteger;28import java.util.concurrent.locks.Lock;29import java.util.concurrent.locks.ReentrantLock;30import nsk.share.TestFailure;31import nsk.share.gc.GC;32import nsk.share.gc.Memory;33import nsk.share.gc.ThreadedGCTest;34import nsk.share.gc.gp.GarbageProducer;35import nsk.share.gc.gp.GarbageProducer1Aware;36import nsk.share.gc.gp.GarbageProducerAware;37import nsk.share.gc.gp.MemoryStrategy;38import nsk.share.gc.gp.MemoryStrategyAware;39import nsk.share.gc.tree.*;40import nsk.share.log.Log;41import nsk.share.test.ExecutionController;42import nsk.share.test.LocalRandom;4344class Forest {4546// the actual size of TreeNode in bytes in the memory calculated as occupied memory / count of nodes47static int nodeSize;4849static long treeSize;5051private static long allNodesCount;5253/* log from test */54static Log log;555657static int treeHeight;5859static long actuallyMut = 0;60private static Forest instance = new Forest();61private Tree[] trees;62private Lock[] locks;6364private int nodeGarbageSize;6566private GarbageProducer gp;67/*68* Create array of trees occupyng given percent of heap69*/70static Forest createForest(long percent, int heightToSizeRatio, int nodeGarbageSize, GarbageProducer gp, Log _log) {71log = _log;7273long size = Runtime.getRuntime().maxMemory() * percent / 100;74treeHeight = Memory.balancedTreeHeightFromMemory(size, (int) new TreeNode(nodeGarbageSize).getTotalSize());75int ntrees = 0;76while (treeHeight * heightToSizeRatio > ntrees) {77ntrees++;78treeHeight = Memory.balancedTreeHeightFromMemory(size / ntrees, (int) new TreeNode(nodeGarbageSize).getTotalSize());79}8081log.debug("The expected forest paramteres: tree height = " + treeHeight + " number of trees = " + ntrees82+ " size = " + new TreeNode(nodeGarbageSize).getTotalSize());83Tree[] localTrees = new Tree[ntrees * 4];84Lock[] localLocks = new Lock[ntrees * 4];85for (int i = 0; i < ntrees * 4; i++) {86localTrees[i] = new Tree(Memory.makeBalancedTreeNode(treeHeight, nodeGarbageSize, gp));87localLocks[i] = new ReentrantLock();8889int numOfAttempts = 0;90if (Concurrent.getPercentInfoByMBeans() > percent) {91log.debug("Attempt to System.gc() before control check. (" + numOfAttempts++ + ")");92System.gc();93if (Concurrent.getPercentInfoByMBeans() > percent) {94instance.trees = new Tree[i];95instance.locks = new Lock[i];96for (int j = 0; j < i; j++) {97instance.trees[j] = localTrees[j];98instance.locks[j] = localLocks[j];99}100allNodesCount = Memory.balancedTreeNodes(treeHeight) * instance.trees.length;101nodeSize = (int) (ManagementFactory.getMemoryMXBean().getHeapMemoryUsage().getUsed() / allNodesCount);102treeSize = Memory.balancedTreeSize(treeHeight, nodeSize);103instance.where = new AtomicCycleInteger(instance.trees.length);104instance.nodeGarbageSize = nodeGarbageSize;105106log.debug("The forest real paramteres: tree height = " + treeHeight + " number of trees = " + instance.trees.length107+ " number of nodes = " + allNodesCount);108log.debug("Approximate node size = " + nodeSize + " calc = " + instance.trees[0].getRoot().getSize());109return instance;110}111}112}113throw new TestFailure("Should not reach here. The correct exit point is inside cycle");114}115116117int treesCount() {118return trees.length;119}120121long nodesCount() {122return allNodesCount;123}124125126127// Confirms that all trees are balanced and have the correct height.128void checkTrees() {129for (int i = 0; i < trees.length; i++) {130locks[i].lock();131checkTree(trees[i]);132locks[i].unlock();133}134}135136private static void checkTree(Tree tree) {137TreeNode root = tree.getRoot();138int h1 = root.getHeight();139int h2 = root.getShortestPath();140if ((h1 != treeHeight) || (h2 != treeHeight)) {141throw new TestFailure("The tree is not balanced expected " + treeHeight142+ " value = " + h1 + " shortedtPath = " + h2);143}144}145146// Swap subtrees in 2 trees, the the path is used147// as sequence of 1-0 to select subtree (left-reight sequence)148static void swapSubtrees(Tree t1, Tree t2, int depth, int path) {149TreeNode tn1 = t1.getRoot();150TreeNode tn2 = t2.getRoot();151for (int i = 0; i < depth; i++) {152if ((path & 1) == 0) {153tn1 = tn1.getLeft();154tn2 = tn2.getLeft();155} else {156tn1 = tn1.getRight();157tn2 = tn2.getRight();158}159path >>= 1;160}161TreeNode tmp;162if ((path & 1) == 0) {163tmp = tn1.getLeft();164tn1.setLeft(tn2.getLeft());165tn2.setLeft(tmp);166} else {167tmp = tn1.getRight();168tn1.setRight(tn2.getRight());169tn2.setLeft(tmp);170}171}172173174// Interchanges two randomly selected subtrees (of same size and depth) several times175void swapSubtrees(long count) {176for (int i = 0; i < count; i++) {177int index1 = LocalRandom.nextInt(trees.length);178int index2 = LocalRandom.nextInt(trees.length);179int depth = LocalRandom.nextInt(treeHeight);180int path = LocalRandom.nextInt();181locks[index1].lock();182// Skip the round to avoid deadlocks183if (locks[index2].tryLock()) {184swapSubtrees(trees[index1], trees[index2], depth, path);185actuallyMut += 2;186locks[index2].unlock();187}188locks[index1].unlock();189190}191192}193194195static class AtomicCycleInteger extends AtomicInteger {196private int max;197public AtomicCycleInteger(int cycleLength) {198super();199this.max = cycleLength - 1;200}201public int cycleIncrementAndGet() {202for (;;) {203int current = get();204int next = (current == max ? 0 : current + 1);205if (compareAndSet(current, next)) {206return next;207}208}209}210}211212// the index in tree array which should be chnaged during next regeneration213AtomicCycleInteger where = null;214215// generate new full and partial trees in our forest216void regenerateTrees(long nodesCount) {217int full = (int) (nodesCount / Memory.balancedTreeNodes(treeHeight)) ;218int partial = (int) nodesCount % (Memory.balancedTreeNodes(treeHeight));219for (int i = 0; i < full; i++) {220int idx = where.cycleIncrementAndGet();221locks[idx].lock();222trees[idx] = new Tree(Memory.makeBalancedTreeNode(treeHeight, nodeGarbageSize));223locks[idx].unlock();224}225while (partial > 0) {226int h = Memory.balancedTreeHeightFromNodes(partial);227Tree newTree = new Tree(Memory.makeBalancedTreeNode(h, nodeGarbageSize));228int idx = where.cycleIncrementAndGet();229locks[idx].lock();230replaceTree(trees[idx], newTree);231locks[idx].unlock();232partial = partial - Memory.balancedTreeNodes(h);233}234}235236237// Given a balanced tree full and a smaller balanced tree partial,238// replaces an appropriate subtree of full by partial, taking care239// to preserve the shape of the full tree.240private static void replaceTree(Tree full, Tree partial) {241boolean dir = (partial.getHeight() % 2) == 0;242actuallyMut++;243replaceTreeWork(full.getRoot(), partial.getRoot(), dir);244}245246// Called only by replaceTree (below) and by itself.247static void replaceTreeWork(TreeNode full, TreeNode partial,248boolean dir) {249boolean canGoLeft = full.getLeft() != null && full.getLeft().getHeight() > partial.getHeight();250boolean canGoRight = full.getRight() != null && full.getRight().getHeight() > partial.getHeight();251if (canGoLeft && canGoRight) {252if (dir) {253replaceTreeWork(full.getLeft(), partial, !dir);254} else {255replaceTreeWork(full.getRight(), partial, !dir);256}257} else if (!canGoLeft && !canGoRight) {258if (dir) {259full.setLeft(partial);260} else {261full.setRight(partial);262}263} else if (!canGoLeft) {264full.setLeft(partial);265} else {266full.setRight(partial);267}268}269270271272}273public class Concurrent extends ThreadedGCTest implements GarbageProducerAware, GarbageProducer1Aware, MemoryStrategyAware {274275// Heap as tree276Forest forest;277278// GP for old gargbage production279GarbageProducer gpOld;280281// GP for young gargbage production282GarbageProducer gpYoung;283284MemoryStrategy ms;285286private void printStatistics() {287log.debug("Actual mutations = " + forest.actuallyMut);288}289290private class Worker implements Runnable {291292private ExecutionController stresser;293294@Override295public void run() {296if (stresser == null) {297stresser = getExecutionController();298}299while (stresser.continueExecution()) {300doStep();301}302}303}304305@Override306public Runnable createRunnable(int i) {307return new Worker();308}309310public static int getPercentInfoByMBeans() {311MemoryMXBean mbean = ManagementFactory.getMemoryMXBean();312return (int) (100 * mbean.getHeapMemoryUsage().getUsed() / mbean.getHeapMemoryUsage().getMax());313}314315private void printMem(long used, long max, String source) {316log.debug("The Memory after allocation (" + source + "): ");317log.debug("Used = " + used + " Max = " + max + " Percent = " + (100 * used / max));318}319320// Command-line parameters.321// young garbage in percent and absolute322private static int youngPercent = 0;323long youngGarbageSize;324// mutation rate (parcent and absolute trees)325private static int ptrMutRate = 50;326long mutateTrees;327// percent of heap to occupy by forest (long live garbage)328private static int livePercent = 60;329// the minimum of which should be available for forest330// test fails if it is not possible to use 60% of heap331private static final int MIN_AVAILABLE_MEM = 60;332// percent of forest to reallocate each step333private static int reallocatePercent = 30;334long reallocateSizeInNodes;335// sleep time in ms336private static int sleepTime = 100;337338private void init(int longLivePercent) {339int numberOfThreads = runParams.getNumberOfThreads();340forest = Forest.createForest(longLivePercent, numberOfThreads,341(int) Math.sqrt(ms.getSize(Runtime.getRuntime().maxMemory())), gpOld, log);342343youngGarbageSize = Runtime.getRuntime().maxMemory() * youngPercent / 100 / numberOfThreads;344reallocateSizeInNodes = forest.nodesCount() * reallocatePercent / 100 / numberOfThreads;345mutateTrees = forest.treesCount() * ptrMutRate / 100 / numberOfThreads / 2;346347log.debug("Young Gen = " + youngGarbageSize);348log.debug("Forest contains " + forest.treesCount() + " trees and " + forest.nodesCount() + " nodes.");349log.debug("Count of nodes to reallocate = " + reallocateSizeInNodes);350log.debug("Count of tree pairs to exchange nodes = " + mutateTrees);351log.debug("Sleep time = " + sleepTime);352353// print some info354MemoryUsage mbean = ManagementFactory.getMemoryMXBean().getHeapMemoryUsage();355printMem(mbean.getUsed(), mbean.getMax(), "Beans");356printMem(Runtime.getRuntime().maxMemory() - Runtime.getRuntime().freeMemory(),357Runtime.getRuntime().maxMemory(), "System");358}359360@Override361public void run() {362try {363init(livePercent);364} catch (OutOfMemoryError oome) {365if (livePercent > MIN_AVAILABLE_MEM) {366log.debug("Unable to use " + livePercent + " use only " + MIN_AVAILABLE_MEM);367init(MIN_AVAILABLE_MEM);368}369}370super.run();371printStatistics();372}373374375376private void doStep() {377// allocate some young garbage378if (youngGarbageSize != 0) {379gpYoung.create(youngGarbageSize);380}381382// allocate some long-live garbage (attached to our trees)383forest.regenerateTrees(reallocateSizeInNodes);384385// mutate pointers386forest.swapSubtrees(mutateTrees);387388// sleep to give GC time for some concurrent actions389try {390Thread.sleep(sleepTime);391} catch (InterruptedException ie) {392}393394// verify trees, also read all pointers395forest.checkTrees();396}397398public static void main(String[] args) {399init(args);400GC.runTest(new Concurrent(), args);401}402403public static void init(String[] args) {404for (int i = 0; i < args.length; ++i) {405if (args[i].equals("-lp")) {406// percent of long lived objects407livePercent = Integer.parseInt(args[++i]);408} else if (args[i].equals("-rp")) {409// percent of trees to reallocate410reallocatePercent = Integer.parseInt(args[++i]);411} else if (args[i].equals("-yp")) {412// percent of young objects413youngPercent = Integer.parseInt(args[++i]);414} else if (args[i].equals("-mr")) {415// percent of trees to exchange (mutate)416ptrMutRate = Integer.parseInt(args[++i]);417} else if (args[i].equals("-st")) {418// sleep time in ms419sleepTime = Integer.parseInt(args[++i]);420}421}422}423424@Override425public void setGarbageProducer(GarbageProducer gp) {426this.gpOld = gp;427}428429430@Override431public void setGarbageProducer1(GarbageProducer gpYoung) {432this.gpYoung = gpYoung;433}434435@Override436public void setMemoryStrategy(MemoryStrategy ms) {437this.ms = ms;438}439}440441442