1 /*
   2  * Copyright (c) 2018, Google 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 package MyPackage;
  25 
  26 import java.util.ArrayList;
  27 import java.util.List;
  28 
  29 /** API for handling the underlying heap sampling monitoring system. */
  30 public class HeapMonitor {
  31   private static int[][] arrays;
  32 
  33   static {
  34     try {
  35       System.loadLibrary("HeapMonitor");
  36     } catch (UnsatisfiedLinkError ule) {
  37       System.err.println("Could not load HeapMonitor library");
  38       System.err.println("java.library.path: " + System.getProperty("java.library.path"));
  39       throw ule;
  40     }
  41   }
  42 
  43   /** Enable heap monitoring sampling with default value for rate. */
  44   public static void enableSamplingRate() {
  45     setSamplingRate(1 << 19);
  46   }
  47 
  48   public static void disableSamplingRate() {
  49     setSamplingRate(0);
  50   }
  51 
  52   /** Set a specific sampling rate, 0 turns off sampling. */
  53   public native static void setSamplingRate(int rate);
  54   /** Set a specific garbage history buffer. */
  55   public native static void setGarbageHistory(int amount);
  56 
  57   public native static void enableSamplingEvents();
  58   public native static void disableSamplingEvents();
  59 
  60   /**
  61    * Allocate memory but first create a stack trace of a particular depth.
  62    *
  63    * @return list of frames for the allocation.
  64    */
  65   public static List<Frame> allocate(int depth) {
  66     List<Frame> frames = new ArrayList<Frame>();
  67     if (depth > 1) {
  68       createStackDepth(depth - 1, frames);
  69       frames.add(new Frame("allocate", "(I)Ljava/util/List;", "HeapMonitor.java", 68));
  70     } else {
  71       actuallyAllocate();
  72       frames.add(new Frame("actuallyAllocate", "()I", "HeapMonitor.java", 131));
  73       frames.add(new Frame("allocate", "(I)Ljava/util/List;", "HeapMonitor.java", 71));
  74     }
  75     return frames;
  76   }
  77 
  78   /**
  79    * Allocate memory but first create a stack trace.
  80    *
  81    * @return list of frames for the allocation.
  82    */
  83   public static List<Frame> allocate() {
  84     int sum = 0;
  85     List<Frame> frames = new ArrayList<Frame>();
  86     allocate(frames);
  87     frames.add(new Frame("allocate", "()Ljava/util/List;", "HeapMonitor.java", 86));
  88     return frames;
  89   }
  90 
  91   private static void createStackDepth(int depth, List<Frame> frames) {
  92     if (depth > 1) {
  93       createStackDepth(depth - 1, frames);
  94       frames.add(new Frame("createStackDepth", "(ILjava/util/List;)V", "HeapMonitor.java", 93));
  95     } else {
  96       allocate(frames);
  97       frames.add(new Frame("createStackDepth", "(ILjava/util/List;)V", "HeapMonitor.java", 96));
  98     }
  99   }
 100 
 101   private static void allocate(List<Frame> frames) {
 102     int sum = 0;
 103     for (int j = 0; j < 1000; j++) {
 104       sum += actuallyAllocate();
 105     }
 106     frames.add(new Frame("actuallyAllocate", "()I", "HeapMonitor.java", 131));
 107     frames.add(new Frame("allocate", "(Ljava/util/List;)V", "HeapMonitor.java", 104));
 108   }
 109 
 110   public static List<Frame> repeatAllocate(int max) {
 111     List<Frame> frames = null;
 112     for (int i = 0; i < max; i++) {
 113       frames = allocate();
 114     }
 115     frames.add(new Frame("repeatAllocate", "(I)Ljava/util/List;", "HeapMonitor.java", 113));
 116     return frames;
 117   }
 118 
 119   private static int actuallyAllocate() {
 120     int sum = 0;
 121 
 122     // Let us assume that a 1-element array is 24 bytes of memory and we want
 123     // 2MB allocated.
 124     int iterations = (1 << 19) / 6;
 125 
 126     if (arrays == null) {
 127       arrays = new int[iterations][];
 128     }
 129 
 130     for (int i = 0; i < iterations; i++) {
 131       int tmp[] = new int[1];
 132       // Force it to be kept and, at the same time, wipe out any previous data.
 133       arrays[i] = tmp;
 134       sum += arrays[0][0];
 135     }
 136     return sum;
 137   }
 138 
 139   public static int allocateSize(int totalSize) {
 140     int sum = 0;
 141 
 142     // Let us assume that a 1-element array is 24 bytes.
 143     int iterations = totalSize / 24;
 144 
 145     if (arrays == null) {
 146       arrays = new int[iterations][];
 147     }
 148 
 149     System.out.println("Allocating for " + iterations);
 150     for (int i = 0; i < iterations; i++) {
 151       int tmp[] = new int[1];
 152 
 153       // Force it to be kept and, at the same time, wipe out any previous data.
 154       arrays[i] = tmp;
 155       sum += arrays[0][0];
 156     }
 157 
 158     return sum;
 159   }
 160 
 161   /** Remove the reference to the global array to free data at the next GC. */
 162   public static void freeStorage() {
 163     arrays = null;
 164   }
 165 
 166   public native static boolean obtainedEvents(Frame[] frames);
 167   public native static boolean garbageContains(Frame[] frames);
 168   public native static boolean eventStorageIsEmpty();
 169   public native static void resetEventStorage();
 170   public native static int getEventStorageElementCount();
 171   public native static void forceGarbageCollection();
 172 
 173   public static boolean statsHaveExpectedNumberSamples(int expected, int acceptedErrorPercentage) {
 174     double actual = getEventStorageElementCount();
 175     double diffPercentage = Math.abs(actual - expected) / expected;
 176     return diffPercentage < acceptedErrorPercentage;
 177   }
 178 }