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