1 /*
   2  * Copyright (c) 2014, 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 import java.lang.management.MemoryPoolMXBean;
  25 import java.util.Optional;
  26 
  27 import sun.hotspot.WhiteBox;
  28 
  29 /**
  30  * Helper class aimed to provide information about alignment of objects in
  31  * particular heap space, expected memory usage after objects' allocation so on.
  32  */
  33 public class AlignmentHelper {
  34     private static final WhiteBox WHITE_BOX = WhiteBox.getWhiteBox();
  35 
  36     private static final long OBJECT_ALIGNMENT_IN_BYTES_FOR_32_VM = 8L;
  37 
  38     /**
  39      * Max relative allowed actual memory usage deviation from expected memory
  40      * usage.
  41      */
  42     private static final float MAX_RELATIVE_DEVIATION = 0.05f; // 5%
  43 
  44     public static final long OBJECT_ALIGNMENT_IN_BYTES = Optional.ofNullable(
  45             AlignmentHelper.WHITE_BOX.getIntxVMFlag("ObjectAlignmentInBytes"))
  46             .orElse(AlignmentHelper.OBJECT_ALIGNMENT_IN_BYTES_FOR_32_VM);
  47 
  48     public static final long SURVIVOR_ALIGNMENT_IN_BYTES = Optional.ofNullable(
  49             AlignmentHelper.WHITE_BOX.getIntxVMFlag("SurvivorAlignmentInBytes"))
  50             .orElseThrow(() ->new AssertionError(
  51                     "Unable to get SurvivorAlignmentInBytes value"));
  52     /**
  53      * Min amount of memory that will be occupied by an object.
  54      */
  55     public static final long MIN_OBJECT_SIZE
  56             = AlignmentHelper.WHITE_BOX.getObjectSize(new Object());
  57     /**
  58      * Min amount of memory that will be occupied by an empty byte array.
  59      */
  60     public static final long MIN_ARRAY_SIZE
  61             = AlignmentHelper.WHITE_BOX.getObjectSize(new byte[0]);
  62 
  63     /**
  64      * Precision at which actual memory usage in a heap space represented by
  65      * this sizing helper could be measured.
  66      */
  67     private final long memoryUsageMeasurementPrecision;
  68     /**
  69      * Min amount of memory that will be occupied by an object allocated in a
  70      * heap space represented by this sizing helper.
  71      */
  72     private final long minObjectSizeInThisSpace;
  73     /**
  74      * Object's alignment in a heap space represented by this sizing helper.
  75      */
  76     private final long objectAlignmentInThisRegion;
  77     /**
  78      * MemoryPoolMXBean associated with a heap space represented by this sizing
  79      * helper.
  80      */
  81     private final MemoryPoolMXBean poolMXBean;
  82 
  83     private static long alignUp(long value, long alignment) {
  84         return ((value - 1) / alignment + 1) * alignment;
  85     }
  86 
  87     protected AlignmentHelper(long memoryUsageMeasurementPrecision,
  88             long objectAlignmentInThisRegion, long minObjectSizeInThisSpace,
  89             MemoryPoolMXBean poolMXBean) {
  90         this.memoryUsageMeasurementPrecision = memoryUsageMeasurementPrecision;
  91         this.minObjectSizeInThisSpace = minObjectSizeInThisSpace;
  92         this.objectAlignmentInThisRegion = objectAlignmentInThisRegion;
  93         this.poolMXBean = poolMXBean;
  94     }
  95 
  96     /**
  97      * Returns how many objects have to be allocated to fill
  98      * {@code memoryToFill} bytes in this heap space using objects of size
  99      * {@code objectSize}.
 100      */
 101     public int getObjectsCount(long memoryToFill, long objectSize) {
 102         return (int) (memoryToFill / getObjectSizeInThisSpace(objectSize));
 103     }
 104 
 105     /**
 106      * Returns amount of memory that {@code objectsCount} of objects with size
 107      * {@code objectSize} will occupy this this space after allocation.
 108      */
 109     public long getExpectedMemoryUsage(long objectSize, int objectsCount) {
 110         long correctedObjectSize = getObjectSizeInThisSpace(objectSize);
 111         return AlignmentHelper.alignUp(correctedObjectSize * objectsCount,
 112                 memoryUsageMeasurementPrecision);
 113     }
 114 
 115     /**
 116      * Returns current memory usage in this heap space.
 117      */
 118     public long getActualMemoryUsage() {
 119         return poolMXBean.getUsage().getUsed();
 120     }
 121 
 122     /**
 123      * Returns maximum memory usage deviation from {@code expectedMemoryUsage}
 124      * given the max allowed relative deviation equal to
 125      * {@code relativeDeviation}.
 126      *
 127      * Note that value returned by this method is aligned according to
 128      * memory measurement precision for this heap space.
 129      */
 130     public long getAllowedMemoryUsageDeviation(long expectedMemoryUsage) {
 131         long unalignedDeviation = (long) (expectedMemoryUsage *
 132                 AlignmentHelper.MAX_RELATIVE_DEVIATION);
 133         return AlignmentHelper.alignUp(unalignedDeviation,
 134                 memoryUsageMeasurementPrecision);
 135     }
 136 
 137     /**
 138      * Returns amount of memory that will be occupied by an object with size
 139      * {@code objectSize} in this heap space.
 140      */
 141     public long getObjectSizeInThisSpace(long objectSize) {
 142         objectSize = Math.max(objectSize, minObjectSizeInThisSpace);
 143 
 144         long alignedObjectSize = AlignmentHelper.alignUp(objectSize,
 145                 objectAlignmentInThisRegion);
 146         long sizeDiff = alignedObjectSize - objectSize;
 147 
 148         // If there is not enough space to fit padding object, then object will
 149         // be aligned to {@code 2 * objectAlignmentInThisRegion}.
 150         if (sizeDiff >= AlignmentHelper.OBJECT_ALIGNMENT_IN_BYTES
 151                 && sizeDiff < AlignmentHelper.MIN_OBJECT_SIZE) {
 152             alignedObjectSize += AlignmentHelper.MIN_OBJECT_SIZE;
 153             alignedObjectSize = AlignmentHelper.alignUp(alignedObjectSize,
 154                     objectAlignmentInThisRegion);
 155         }
 156 
 157         return alignedObjectSize;
 158     }
 159     @Override
 160     public String toString() {
 161         StringBuilder builder = new StringBuilder();
 162 
 163         builder.append(String.format("AlignmentHelper for memory pool '%s':%n",
 164                 poolMXBean.getName()));
 165         builder.append(String.format("Memory usage measurement precision: %d%n",
 166                 memoryUsageMeasurementPrecision));
 167         builder.append(String.format("Min object size in this space: %d%n",
 168                 minObjectSizeInThisSpace));
 169         builder.append(String.format("Object alignment in this space: %d%n",
 170                 objectAlignmentInThisRegion));
 171 
 172         return builder.toString();
 173     }
 174 }