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   /** Set a specific sampling rate, 0 samples every allocation. */
  44   public native static void setSamplingRate(int rate);
  45 
  46   public native static void enableSamplingEvents();
  47   public native static void disableSamplingEvents();
  48 
  49   /**
  50    * Allocate memory but first create a stack trace of a particular depth.
  51    *
  52    * @return list of frames for the allocation.
  53    */
  54   public static List<Frame> allocate(int depth) {
  55     List<Frame> frames = new ArrayList<Frame>();
  56     if (depth > 1) {
  57       createStackDepth(depth - 1, frames);
  58       frames.add(new Frame("allocate", "(I)Ljava/util/List;", "HeapMonitor.java", 57));
  59     } else {
  60       actuallyAllocate();
  61       frames.add(new Frame("actuallyAllocate", "()I", "HeapMonitor.java", 120));
  62       frames.add(new Frame("allocate", "(I)Ljava/util/List;", "HeapMonitor.java", 60));
  63     }
  64     return frames;
  65   }
  66 
  67   /**
  68    * Allocate memory but first create a stack trace.
  69    *
  70    * @return list of frames for the allocation.
  71    */
  72   public static List<Frame> allocate() {
  73     int sum = 0;
  74     List<Frame> frames = new ArrayList<Frame>();
  75     allocate(frames);
  76     frames.add(new Frame("allocate", "()Ljava/util/List;", "HeapMonitor.java", 75));
  77     return frames;
  78   }
  79 
  80   private static void createStackDepth(int depth, List<Frame> frames) {
  81     if (depth > 1) {
  82       createStackDepth(depth - 1, frames);
  83       frames.add(new Frame("createStackDepth", "(ILjava/util/List;)V", "HeapMonitor.java", 82));
  84     } else {
  85       allocate(frames);
  86       frames.add(new Frame("createStackDepth", "(ILjava/util/List;)V", "HeapMonitor.java", 85));
  87     }
  88   }
  89 
  90   private static void allocate(List<Frame> frames) {
  91     int sum = 0;
  92     for (int j = 0; j < 1000; j++) {
  93       sum += actuallyAllocate();
  94     }
  95     frames.add(new Frame("actuallyAllocate", "()I", "HeapMonitor.java", 120));
  96     frames.add(new Frame("allocate", "(Ljava/util/List;)V", "HeapMonitor.java", 93));
  97   }
  98 
  99   public static List<Frame> repeatAllocate(int max) {
 100     List<Frame> frames = null;
 101     for (int i = 0; i < max; i++) {
 102       frames = allocate();
 103     }
 104     frames.add(new Frame("repeatAllocate", "(I)Ljava/util/List;", "HeapMonitor.java", 102));
 105     return frames;
 106   }
 107 
 108   private static int actuallyAllocate() {
 109     int sum = 0;
 110 
 111     // Let us assume that a 1-element array is 24 bytes of memory and we want
 112     // 2MB allocated.
 113     int iterations = (1 << 19) / 6;
 114 
 115     if (arrays == null) {
 116       arrays = new int[iterations][];
 117     }
 118 
 119     for (int i = 0; i < iterations; i++) {
 120       int tmp[] = new int[1];
 121       // Force it to be kept and, at the same time, wipe out any previous data.
 122       arrays[i] = tmp;
 123       sum += arrays[0][0];
 124     }
 125     return sum;
 126   }
 127 
 128   public static int allocateSize(int totalSize) {
 129     int sum = 0;
 130 
 131     // Let us assume that a 1-element array is 24 bytes.
 132     int iterations = totalSize / 24;
 133 
 134     if (arrays == null) {
 135       arrays = new int[iterations][];
 136     }
 137 
 138     System.out.println("Allocating for " + iterations);
 139     for (int i = 0; i < iterations; i++) {
 140       int tmp[] = new int[1];
 141 
 142       // Force it to be kept and, at the same time, wipe out any previous data.
 143       arrays[i] = tmp;
 144       sum += arrays[0][0];
 145     }
 146 
 147     return sum;
 148   }
 149 
 150   /** Remove the reference to the global array to free data at the next GC. */
 151   public static void freeStorage() {
 152     arrays = null;
 153   }
 154 
 155   public native static boolean obtainedEvents(Frame[] frames);
 156   public native static boolean garbageContains(Frame[] frames);
 157   public native static boolean eventStorageIsEmpty();
 158   public native static void resetEventStorage();
 159   public native static int getEventStorageElementCount();
 160   public native static void forceGarbageCollection();
 161 
 162   public static boolean statsHaveExpectedNumberSamples(int expected, int acceptedErrorPercentage) {
 163     double actual = getEventStorageElementCount();
 164     double diffPercentage = Math.abs(actual - expected) / expected;
 165     return diffPercentage < acceptedErrorPercentage;
 166   }
 167 }