1 /*
   2  * Copyright (c) 2017, 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 package utils;
  24 
  25 import java.lang.management.ManagementFactory;
  26 import java.lang.management.MemoryPoolMXBean;
  27 import java.lang.management.MemoryUsage;
  28 import java.util.ArrayList;
  29 import java.util.List;
  30 
  31 /**
  32  * This is an class used to allocate specified amount of metaspace and heap.
  33  * Object of this class should be created before any garbage collection cycles because GC can
  34  * shrink heap and Runtime.maxMemory() might report unexpected value.
  35  */
  36 public class GarbageProducer{
  37 
  38     // Uses fixed small objects to avoid Humongous objects allocation with G1 GC.
  39     public static final int MEMORY_CHUNK = 2048;
  40 
  41     public static List<Object> allocatedMetaspace;
  42     public static List<Object> allocatedMemory;
  43 
  44     private final Runtime runtime;
  45     private final float targetMemoryUsagePercent;
  46     private final long garbageToAllocate;
  47 
  48     /**
  49      * @param targetMemoryUsagePercent how many percent of metaspace and heap to allocate
  50      */
  51     public GarbageProducer(float targetMemoryUsagePercent) {
  52         this.targetMemoryUsagePercent = targetMemoryUsagePercent;
  53         runtime = Runtime.getRuntime();
  54         // Calculate amount of garbage to occupate targetMemoryUsagePercent of heap
  55         garbageToAllocate = runtime.freeMemory()
  56             - (long) ((1.0 - targetMemoryUsagePercent) * runtime.maxMemory());
  57     }
  58 
  59     /**
  60      * Allocates heap and metaspace upon exit targetMemoryUsagePercent percents
  61      * of heap and metaspace have been consumed.
  62      */
  63     public void allocateMetaspaceAndHeap() {
  64         // Metaspace should be filled before Java Heap to prevent unexpected OOME
  65         // in the Java Heap while filling Metaspace
  66         allocatedMetaspace = eatMetaspace(targetMemoryUsagePercent);
  67         allocatedMemory = allocateGarbage(garbageToAllocate);
  68     }
  69 
  70     private List<Object> eatMetaspace(float targetUsage) {
  71         List<Object> list = new ArrayList<>();
  72         final String metaspacePoolName = "Metaspace";
  73         MemoryPoolMXBean metaspacePool = null;
  74         for (MemoryPoolMXBean pool : ManagementFactory.getMemoryPoolMXBeans()) {
  75             if (pool.getName().contains(metaspacePoolName)) {
  76                 metaspacePool = pool;
  77                 break;
  78             }
  79         }
  80         if (metaspacePool == null) {
  81             throw new RuntimeException("MXBean for Metaspace pool wasn't found");
  82         }
  83         float currentUsage;
  84         GeneratedClassProducer gp = new GeneratedClassProducer();
  85         do {
  86             try {
  87                 list.add(gp.create(0));
  88             } catch (OutOfMemoryError oome) {
  89                 list = null;
  90                 throw new RuntimeException("Unexpected OOME '" + oome.getMessage() + "' while eating " + targetUsage + " of Metaspace.");
  91             }
  92             MemoryUsage memoryUsage = metaspacePool.getUsage();
  93             currentUsage = (((float) memoryUsage.getUsed()) / memoryUsage.getMax());
  94         } while (currentUsage < targetUsage);
  95         return list;
  96     }
  97 
  98     private List<Object> allocateGarbage(long garbageToAllocate) {
  99         long allocated = 0l;
 100         List<Object> list = new ArrayList<>();
 101         do {
 102             try {
 103                 list.add(new byte[MEMORY_CHUNK]);
 104                 allocated += MEMORY_CHUNK;
 105             } catch (OutOfMemoryError e) {
 106                 list = null;
 107                 throw new RuntimeException("Unexpected OOME '" + e.getMessage() + "'. Allocated " + allocated + " bytes.");
 108             }
 109         } while (allocated < garbageToAllocate);
 110         return list;
 111     }
 112 }