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
24import java.lang.management.MemoryPoolMXBean;
25import java.util.Optional;
26
27import 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 */
33public 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}
175