1 /*
   2  * Copyright (c) 2004, 2018, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.
   8  *
   9  * This code is distributed in the hope that it will be useful, but WITHOUT
  10  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  11  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  12  * version 2 for more details (a copy is included in the LICENSE file that
  13  * accompanied this code).
  14  *
  15  * You should have received a copy of the GNU General Public License version
  16  * 2 along with this work; if not, write to the Free Software Foundation,
  17  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  18  *
  19  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  20  * or visit www.oracle.com if you need additional information or have any
  21  * questions.
  22  */
  23 
  24 /*
  25  * @test
  26  * @key stress gc
  27  *
  28  * @summary converted from VM Testbase gc/gctests/LargeObjects/large001.
  29  * VM Testbase keywords: [gc, stress, stressopt, nonconcurrent, quick]
  30  * VM Testbase readme:
  31  * DESCRIPTION
  32  *     The test checks that Garbage Collector correctly does not throw any
  33  *     unexpected exceptions/errors while allocating large objects (classes
  34  *     that have more than 65535 fields and classes that have less than 65535
  35  *     fields). 65535 of fields is a limitation for JVM (see JVM specification
  36  *     Second edition 4.10).
  37  *     Since it is impossible to create one class with about 65535 of fields
  38  *     (javac cannot compile it), a child class extends a parent class, so the
  39  *     fields are devided into two subsets. However, the child class still has
  40  *     about 65535 of fields.
  41  *     The test starts a number of threads. This number is either set in *.cfg
  42  *     file or is calculated by the test itself based on the machine (see
  43  *     nsk.share.gc.Algorithms.getThreadsCount() method). As soon as all threads
  44  *     are started, each thread begins its checking.
  45  *     There are 13 classes to be loaded by each thread. These classes are
  46  *     generated by nsk.share.gc.Generator (see its javadoc for more details).
  47  *     Each class has a huge number of fields, but this number is less than the JVM
  48  *     limitation.
  49  *     The test loads the classes with nsk.share.gc.GCClassUnloader class that
  50  *     extends nsk.share.ClassUnloader and has a bit different algorith of eating
  51  *     heap. As soon as a class is loaded, the test creates an instance of
  52  *     it - allocates an object of that type. Then it drops references to the
  53  *     class and to the instance and tries to unload the class. The test does not
  54  *     expect any exceptions to be thrown.
  55  *
  56  * @library /vmTestbase
  57  *          /test/lib
  58  * @run driver jdk.test.lib.FileInstaller . .
  59  *
  60  * @comment generate and compile nsk.share.gc.newclass.* classes
  61  * @run driver nsk.share.gc.GenClassesBuilder
  62  *
  63  * @run main/othervm
  64  *      -XX:-UseGCOverheadLimit
  65  *      -Xlog:gc*
  66  *      gc.gctests.LargeObjects.large001.large001
  67  *      -largeClassesPath classes
  68  *      -isOverLimitFields false
  69  *      -aggregationDepth 0
  70  *      -t 1
  71  */
  72 
  73 package gc.gctests.LargeObjects.large001;
  74 
  75 import java.lang.reflect.*;
  76 import java.lang.ref.WeakReference;
  77 import java.util.*;
  78 import nsk.share.TestFailure;
  79 
  80 
  81 import nsk.share.gc.*;
  82 import nsk.share.*;
  83 
  84 public class large001 extends ThreadedGCTest {
  85 
  86     // Package of the classes to be loaded
  87     final static String PREFIX = "nsk.share.gc.newclass.";
  88     // A bunch of classes that have number of fields more than JVM limitation
  89     final static String[] LCLASSES = {PREFIX + "private_int_lchild",
  90         PREFIX + "protected_short_lchild",
  91         PREFIX + "public_long_lchild",
  92         PREFIX + "public_Object_lchild",
  93         PREFIX + "static_byte_lchild",
  94         PREFIX + "static_float_lchild",
  95         PREFIX + "transient_boolean_lchild",
  96         PREFIX + "volatile_double_lchild",
  97         PREFIX + "protected_combination_lchild",
  98         PREFIX + "public_combination_lchild",
  99         PREFIX + "static_combination_lchild",
 100         PREFIX + "transient_combination_lchild",
 101         PREFIX + "volatile_combination_lchild"
 102     };
 103     // A bunch of classes that have number of fields less than JVM limitation
 104     final static String[] SCLASSES = {PREFIX + "private_int_schild",
 105         PREFIX + "protected_short_schild",
 106         PREFIX + "public_long_schild",
 107         PREFIX + "public_Object_schild",
 108         PREFIX + "static_byte_schild",
 109         PREFIX + "static_float_schild",
 110         PREFIX + "transient_boolean_schild",
 111         PREFIX + "volatile_double_schild",
 112         PREFIX + "protected_combination_schild",
 113         PREFIX + "public_combination_schild",
 114         PREFIX + "static_combination_schild",
 115         PREFIX + "transient_combination_schild",
 116         PREFIX + "volatile_combination_schild"
 117     };
 118     boolean isOverLimitFields = true;
 119     int aggregationDepth = 0;
 120     String largeClassesPath;
 121 
 122     private class Worker implements Runnable {
 123 
 124         int id;
 125 
 126         public Worker(int id) {
 127             this.id = id;
 128         }
 129 
 130         public void run() {
 131             try {
 132                 // Use special ClassUnloader to load/unload classes
 133                 ClassUnloader unloader = new ClassUnloader();
 134                 String[] classes = isOverLimitFields ? LCLASSES : SCLASSES;
 135 
 136                 for (String name : classes) {
 137                     // Load the class
 138                     log.debug(id + ": Loading class: " + name);
 139                     unloader.loadClass(name, largeClassesPath);
 140                     log.debug(id + ": Class loaded: " + name);
 141 
 142                     Class loadedClass = unloader.getLoadedClass();
 143                     Object loadedClassInstance = loadedClass.newInstance();
 144 
 145                     log.debug(id + ": Instance of the class: " + loadedClassInstance);
 146                     int depth = aggregationDepth;
 147                     List<WeakReference> refs = new ArrayList<WeakReference>(depth);
 148                     addObjRef(loadedClassInstance, loadedClass, depth, refs);
 149 
 150                     // Drop all references to the class and try to unload it
 151                     Algorithms.eatMemory(getExecutionController());
 152                     log.debug(id + ": Testing non-null after GC force for: " + name);
 153                     if (loadedClass == null || loadedClassInstance == null) {
 154                         throw new Exception("Null class");
 155                     }
 156                     verifyObjRef(loadedClassInstance, depth);
 157                     for (WeakReference ref : refs) {
 158                         if (ref.get() == null) {
 159                             throw new Exception("Unexpected null reference");
 160                         }
 161                     }
 162                     refs = null;
 163                     loadedClass = null;
 164                     loadedClassInstance = null;
 165 
 166                     log.debug(id + ": Unloading class: "
 167                             + name);
 168                     boolean result = unloader.unloadClass(getExecutionController());
 169                     log.debug(id + ": Result of uloading "
 170                             + "class " + name + ": " + result);
 171                 }
 172             } catch (OutOfMemoryError oome) {
 173                 // just skip if we eat memory in several threads...
 174                 // rethrow in the case of one thread
 175                 if (runParams.getNumberOfThreads() == 1) {
 176                     throw oome;
 177                 }
 178             } catch (Throwable t) {
 179                 throw new TestFailure("Unexpected exception: ", t);
 180             }
 181         }
 182 
 183         // This method recursively create chain of aggregated objects for given object
 184         public void addObjRef(Object object, Class clazz, int count, List<WeakReference> list) throws Throwable {
 185             if (count == 0) {
 186                 return;
 187             }
 188 
 189             Field[] fields = object.getClass().getFields();
 190             for (Field field : fields) {
 191                 if (field.getName().startsWith("obj")) {
 192                     Object addedObject = clazz.newInstance();
 193                     field.set(object, addedObject);
 194                     System.out.println("Added field " + field.getName() + "  .... " + count);
 195                     addObjRef(addedObject, clazz, count - 1, list);
 196                     list.add(new WeakReference<Object>(addedObject));
 197                 }
 198             }
 199         }
 200 
 201         // This method recursively verfiy chain of aggregated objects for given object.
 202         // Throws null pointer exception of objP/C field is null
 203         public void verifyObjRef(Object object, int count) throws Throwable {
 204             if (count == 0) {
 205                 return;
 206             }
 207 
 208             Field[] fields = object.getClass().getFields();
 209             for (Field field : fields) {
 210                 if (field.getName().startsWith("obj")) {
 211                     Object obj = field.get(object);
 212                     verifyObjRef(obj, count - 1);
 213                 }
 214             }
 215         }
 216     }
 217 
 218     public large001(String[] args) {
 219         for (int i = 0; i < args.length; i++) {
 220             if (args[i].equals("-largeClassesPath")) {
 221                 largeClassesPath = args[++i];
 222             } else if (args[i].equals("-isOverLimitFields")) {
 223                 isOverLimitFields = Boolean.getBoolean(args[++i]);
 224             } else if (args[i].equals("-aggregationDepth")) {
 225                 aggregationDepth = Integer.parseInt(args[++i]);
 226             }
 227         }
 228         if (largeClassesPath == null || largeClassesPath.length() == 0) {
 229             throw new TestFailure("No classpath for large classes is given");
 230         }
 231     }
 232 
 233     @Override
 234     protected Runnable createRunnable(int i) {
 235         return new Worker(i);
 236     }
 237 
 238     @Override
 239     public void run() {
 240         if (isOverLimitFields) {
 241             log.debug("Loading classes that have number "
 242                     + "of fields over limitation (more "
 243                     + "than 65535)");
 244         } else {
 245             log.debug("Loading classes that have number "
 246                     + "of fields under limitation (less "
 247                     + "than 65535)");
 248         }
 249         super.run();
 250     }
 251 
 252     public static void main(String args[]) {
 253         GC.runTest(new large001(args), args);
 254     }
 255 }