1 /*
  2  * Copyright (c) 2017, 2024, 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 
 25 import java.lang.management.MemoryPoolMXBean;
 26 
 27 import com.sun.jdi.NativeMethodException;
 28 
 29 import jdk.internal.value.ValueClass;
 30 import jdk.internal.vm.annotation.ImplicitlyConstructible;
 31 import jdk.internal.vm.annotation.LooselyConsistentValue;
 32 import jdk.internal.vm.annotation.NullRestricted;
 33 import jdk.test.lib.Asserts;
 34 import jdk.test.whitebox.WhiteBox;
 35 
 36 
 37 /**
 38  * @test InlineTypeDensity
 39  * @summary Heap density test for InlineTypes
 40  * @library /test/lib
 41  * @modules java.base/jdk.internal.vm.annotation
 42  *          java.base/jdk.internal.value
 43  * @enablePreview
 44  * @compile InlineTypeDensity.java
 45  * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox
 46  * @run main/othervm -XX:FlatArrayElementMaxSize=-1 -XX:+UseCompressedOops
 47  *                   -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions
 48  *                    -XX:+WhiteBoxAPI InlineTypeDensity
 49  * @run main/othervm -XX:FlatArrayElementMaxSize=-1 -XX:-UseCompressedOops
 50  *                   -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions
 51  *                    -XX:+WhiteBoxAPI InlineTypeDensity
 52  * @run main/othervm -XX:FlatArrayElementMaxSize=-1
 53  *                   -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions
 54  *                   -XX:+WhiteBoxAPI InlineTypeDensity
 55  * @run main/othervm -XX:+UnlockDiagnosticVMOptions -XX:FlatArrayElementMaxSize=-1
 56  *                   -Xbootclasspath/a:. -XX:ForceNonTearable=*
 57  *                   -XX:+WhiteBoxAPI InlineTypeDensity
 58  */
 59 
 60 public class InlineTypeDensity {
 61 
 62     private static final WhiteBox WHITE_BOX = WhiteBox.getWhiteBox();
 63     private static final boolean VM_FLAG_FORCENONTEARABLE = WHITE_BOX.getStringVMFlag("ForceNonTearable").equals("*");
 64 
 65     public InlineTypeDensity() {
 66         if (WHITE_BOX.getIntxVMFlag("FlatArrayElementMaxSize") != -1) {
 67             throw new IllegalStateException("FlatArrayElementMaxSize should be -1");
 68         }
 69     }
 70 
 71     interface LocalDate {
 72         public int getYear();
 73         public short getMonth();
 74         public short getDay();
 75     }
 76 
 77     interface LocalTime {
 78         public byte getHour();
 79         public byte getMinute();
 80         public byte getSecond();
 81         public int getNano();
 82     }
 83 
 84     interface LocalDateTime extends LocalDate, LocalTime {}
 85 
 86     @ImplicitlyConstructible
 87     @LooselyConsistentValue
 88     static value class LocalDateValue implements LocalDate {
 89         final int   year;
 90         final short month;
 91         final short day;
 92 
 93         public LocalDateValue(int year, short month, short day) {
 94             this.year = year;
 95             this.month = month;
 96             this.day = day;
 97         }
 98 
 99         public int   getYear()  { return year; }
100         public short getMonth() { return month; }
101         public short getDay()   { return day; }
102 
103     }
104 
105     @ImplicitlyConstructible
106     @LooselyConsistentValue
107     static value class LocalTimeValue implements LocalTime {
108         final byte hour;
109         final byte minute;
110         final byte second;
111         final int nano;
112 
113         public LocalTimeValue(byte hour, byte minute, byte second, int nano) {
114             this.hour = hour;
115             this.minute = minute;
116             this.second = second;
117             this.nano = nano;
118         }
119 
120         public byte getHour()   { return hour; }
121         public byte getMinute() { return minute; }
122         public byte getSecond() { return second; }
123         public int getNano()    { return nano; }
124 
125     }
126 
127     @ImplicitlyConstructible
128     @LooselyConsistentValue
129     static value class LocalDateTimeValue implements LocalDateTime {
130         @NullRestricted
131         LocalDateValue date;
132         @NullRestricted
133         LocalTimeValue time;
134 
135         public LocalDateTimeValue(LocalDateValue date, LocalTimeValue time) {
136             this.date = date;
137             this.time = time;
138         }
139 
140         public int   getYear()  { return date.year; }
141         public short getMonth() { return date.month; }
142         public short getDay()   { return date.day; }
143 
144         public byte getHour()   { return time.hour; }
145         public byte getMinute() { return time.minute; }
146         public byte getSecond() { return time.second; }
147         public int getNano()    { return time.nano; }
148 
149     }
150 
151     static final class LocalDateClass implements LocalDate {
152         final int   year;
153         final short month;
154         final short day;
155 
156         LocalDateClass(int year, short month, short day) {
157             this.year  = year;
158             this.month = month;
159             this.day   = day;
160         }
161 
162         public int   getYear()  { return year; }
163         public short getMonth() { return month; }
164         public short getDay()   { return day; }
165     }
166 
167     static final class LocalTimeClass implements LocalTime {
168         final byte hour;
169         final byte minute;
170         final byte second;
171         final int nano;
172 
173         LocalTimeClass(byte hour, byte minute, byte second, int nano) {
174             this.hour   = hour;
175             this.minute = minute;
176             this.second = second;
177             this.nano   = nano;
178         }
179 
180         public byte getHour()   { return hour; }
181         public byte getMinute() { return minute; }
182         public byte getSecond() { return second; }
183         public int getNano()    { return nano; }
184     }
185 
186     static final class LocalDateTimeClass implements LocalDateTime {
187         final LocalDateClass date;
188         final LocalTimeClass time;
189 
190         LocalDateTimeClass(LocalDateClass date, LocalTimeClass time) {
191             this.date = date;
192             this.time = time;
193         }
194 
195         public LocalDateClass getDate() { return date; }
196         public LocalTimeClass getTime() { return time; }
197 
198         public int   getYear()  { return date.year; }
199         public short getMonth() { return date.month; }
200         public short getDay()   { return date.day; }
201 
202         public byte getHour()   { return time.hour; }
203         public byte getMinute() { return time.minute; }
204         public byte getSecond() { return time.second; }
205         public int getNano()    { return time.nano; }
206     }
207 
208     public void ensureArraySizeWin() {
209         int arrayLength = 1000;
210         System.out.println("ensureArraySizeWin for length " + arrayLength);
211         LocalDateTimeClass[] objectArray = new LocalDateTimeClass[arrayLength];
212         for (int i = 0; i < arrayLength; i++) {
213             objectArray[i] = new LocalDateTimeClass(new LocalDateClass(0, (short)0, (short)0),
214                     new LocalTimeClass((byte)0, (byte)0, (byte)0, 0));
215         }
216 
217         long objectArraySize = WHITE_BOX.getObjectSize(objectArray);
218         System.out.println("Empty object array size: " + objectArraySize);
219         objectArraySize += (arrayLength *
220                 (WHITE_BOX.getObjectSize(objectArray[0]) +
221                         WHITE_BOX.getObjectSize(objectArray[0].getDate()) +
222                         WHITE_BOX.getObjectSize(objectArray[0].getTime())));
223 
224         LocalDateTimeValue[] flatArray = new LocalDateTimeValue[arrayLength];
225         // CMH: add "isFlatValueArray" to WhiteBox API, to ensure we are correctly account size
226 
227         long flatArraySize = WHITE_BOX.getObjectSize(flatArray);
228         System.out.println("Object array and elements: " + objectArraySize + " versus Flat Array: " + flatArraySize);
229         Asserts.assertLessThan(flatArraySize, objectArraySize, "Flat array accounts for more heap than object array + elements !");
230     }
231 
232     @ImplicitlyConstructible
233     @LooselyConsistentValue
234     static value class MyByte  { byte  v = 0; }
235     @ImplicitlyConstructible
236     @LooselyConsistentValue
237     static value class MyShort { short v = 0; }
238     @ImplicitlyConstructible
239     @LooselyConsistentValue
240     static value class MyInt   { int   v = 0; }
241     @ImplicitlyConstructible
242     @LooselyConsistentValue
243     static value class MyLong  { long  v = 0; }
244 
245     void assertArraySameSize(Object a, Object b, int nofElements) {
246         long aSize = WHITE_BOX.getObjectSize(a);
247         long bSize = WHITE_BOX.getObjectSize(b);
248         Asserts.assertEquals(aSize, bSize,
249             a + "(" + aSize + " bytes) not equivalent size " +
250             b + "(" + bSize + " bytes)" +
251             (nofElements >= 0 ? " (array of " + nofElements + " elements)" : ""));
252     }
253 
254     void testByteArraySizesSame(int[] testSizes) {
255         for (int testSize : testSizes) {
256             byte[] ba = new byte[testSize];
257             MyByte[] mba = (MyByte[])ValueClass.newNullRestrictedArray(MyByte.class, testSize);
258             assertArraySameSize(ba, mba, testSize);
259         }
260     }
261 
262     void testShortArraySizesSame(int[] testSizes) {
263         for (int testSize : testSizes) {
264             short[] sa = new short[testSize];
265             MyShort[] msa = (MyShort[])ValueClass.newNullRestrictedArray(MyShort.class, testSize);
266             assertArraySameSize(sa, msa, testSize);
267         }
268     }
269 
270     void testIntArraySizesSame(int[] testSizes) {
271         for (int testSize : testSizes) {
272             int[] ia = new int[testSize];
273             MyInt[] mia = (MyInt[])ValueClass.newNullRestrictedArray(MyInt.class, testSize);
274             assertArraySameSize(ia, mia, testSize);
275         }
276     }
277 
278     void testLongArraySizesSame(int[] testSizes) {
279         for (int testSize : testSizes) {
280             long[] la = new long[testSize];
281             MyLong[] mla = (MyLong[])ValueClass.newNullRestrictedArray(MyLong.class, testSize);
282             assertArraySameSize(la, mla, testSize);
283         }
284     }
285 
286     public void testPrimitiveArraySizesSame() {
287         int[] testSizes = new int[] { 0, 1, 2, 3, 4, 7, 10, 257 };
288         testByteArraySizesSame(testSizes);
289         testShortArraySizesSame(testSizes);
290         testIntArraySizesSame(testSizes);
291         testLongArraySizesSame(testSizes);
292     }
293 
294     @ImplicitlyConstructible
295     @LooselyConsistentValue
296     static value class bbValue { byte b = 0; byte b2 = 0;}
297     @ImplicitlyConstructible
298     @LooselyConsistentValue
299     static value class bsValue { byte b = 0; short s = 0;}
300     @ImplicitlyConstructible
301     @LooselyConsistentValue
302     static value class siValue { short s = 0; int i = 0;}
303     @ImplicitlyConstructible
304     @LooselyConsistentValue
305     static value class ssiValue { short s = 0; short s2 = 0; int i = 0;}
306     @ImplicitlyConstructible
307     @LooselyConsistentValue
308     static value class blValue { byte b = 0; long l = 0; }
309 
310     // Expect aligned array addressing to nearest pow2
311     void testAlignedSize() {
312         int testSize = 10;
313         assertArraySameSize(new short[testSize], ValueClass.newNullRestrictedArray(bbValue.class, testSize), testSize);
314         assertArraySameSize(new long[testSize], ValueClass.newNullRestrictedArray(siValue.class, testSize), testSize);
315         assertArraySameSize(new long[testSize], ValueClass.newNullRestrictedArray(ssiValue.class, testSize), testSize);
316         assertArraySameSize(new long[testSize*2], ValueClass.newNullRestrictedArray(blValue.class, testSize), testSize);
317         assertArraySameSize(new int[testSize], ValueClass.newNullRestrictedArray(bsValue.class, testSize), testSize);
318     }
319 
320     public void test() {
321         ensureArraySizeWin();
322         testPrimitiveArraySizesSame();
323         if (!VM_FLAG_FORCENONTEARABLE) {
324           testAlignedSize();
325         }
326     }
327 
328     public static void main(String[] args) {
329         new InlineTypeDensity().test();
330     }
331 
332 }