1 /*
  2  * Copyright (c) 2005, 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 package nsk.share.gc;
 25 
 26 import nsk.share.test.LocalRandom;
 27 import java.io.PrintStream;
 28 import nsk.share.gc.gp.GarbageProducer;
 29 import nsk.share.gc.tree.*;
 30 import nsk.share.gc.gp.MemoryStrategy;
 31 import nsk.share.log.Log;
 32 
 33 /**
 34  *  Different utility methods to work with memory objects.
 35  */
 36 public final class Memory {
 37         private static int bits = 0;
 38         private static int referenceSize = 0;
 39         private static int objectExtraSize = 0;
 40 
 41         private Memory() {
 42         }
 43 
 44         private static int getBits() {
 45                 if (bits == 0)
 46                         bits = Integer.parseInt(System.getProperty("sun.arch.data.model"));
 47                 return bits;
 48         }
 49 
 50         /**
 51          *  Get size of one object reference.
 52          *
 53          *  TODO: somehow determine the real value
 54          */
 55         public static int getReferenceSize() {
 56                 if (referenceSize == 0)
 57                         referenceSize = (getBits() == 64) ? 8 : 4;
 58                 return referenceSize;
 59         }
 60 
 61         /**
 62          * Get size of primitive type int.
 63          */
 64         public static int getIntSize() {
 65                 return 4;
 66         }
 67 
 68         /**
 69          * Get size of primitive type boolean.
 70          */
 71         public static int getBooleanSize() {
 72                 return 1;
 73         }
 74 
 75         /**
 76          * Get size of primitive type byte.
 77          */
 78         public static int getByteSize() {
 79                 return 1;
 80         }
 81 
 82         /**
 83          * Get size of primitive type char.
 84          */
 85         public static int getCharSize() {
 86                 return 2;
 87         }
 88 
 89         /**
 90          * Get size of primitive type short.
 91          */
 92         public static int getShortSize() {
 93                 return 2;
 94         }
 95 
 96         /**
 97          * Get size of primitive type long.
 98          */
 99         public static int getLongSize() {
100                 return 8;
101         }
102 
103         /**
104          * Get size of primitive type float.
105          */
106         public static int getFloatSize() {
107                 return 4;
108         }
109 
110         /**
111          * Get size of primitive type double.
112          */
113         public static int getDoubleSize() {
114                 return 8;
115         }
116 
117         /**
118          *  Get how many extra bytes an object occupies in the heap
119          *  compared to sum of it's fields.
120          *
121          *  TODO: somehow determine the real value
122          */
123         public static int getObjectExtraSize() {
124                 if (objectExtraSize == 0)
125                         objectExtraSize = 2 * getReferenceSize();
126                 return objectExtraSize;
127         }
128         /**
129          *  Get how many extra bytes an array occupies in the heap
130          *  compared to sum of it's fields.
131          *
132          *  TODO: somehow determine the real value
133          */
134         public static int getArrayExtraSize() {
135                 return getObjectExtraSize();
136         }
137 
138         /**
139          * Return size of reference object (SoftReference, WeakReference, PhantomReference)
140          */
141         public static int getReferenceObjectSize() {
142                 return getReferenceSize() + getObjectExtraSize();
143         }
144 
145         /**
146          *  Get an approximate length of array that will occupy a given memory.
147          *
148          *  @param memory size of memory
149          *  @param objectSize size of each object in array
150          *  @return length of array
151          */
152         public static int getArrayLength(long memory, long objectSize) {
153                 int arrayExtraSize = getArrayExtraSize();
154                 return (int) Math.min((memory - arrayExtraSize) / objectSize,
155                         Integer.MAX_VALUE);
156         }
157 
158         /**
159          *  Get an approximate size of array of given length and object size.
160          *
161          *  @param length length of arary
162          *  @param objectSize size of object in array
163          *  @return size of array
164          */
165         public static long getArraySize(int length, long objectSize) {
166                 return getArrayExtraSize() + length * objectSize;
167         }
168 
169         /**
170          *  Calculate approximate size of biggest of MemoryObjects.
171          */
172         public static long getMemoryObjectSize(long size) {
173                 return size + 2 * getReferenceSize() + getObjectExtraSize();
174         }
175 
176         /**
177          *  Calculate approximate size of linked list in memory.
178          *
179          *  @param length length of list
180          *  @param size size of object
181          *  @return size
182          */
183         public static long getListSize(int length, int size) {
184                 return getObjectExtraSize() + length * (getReferenceSize() + getMemoryObjectSize(size));
185         }
186 
187         /**
188          *  Calculate length of linear or circular linked list.
189          *
190          *  @param mobj head of list
191          *  @return length of list
192          */
193         public static int getListLength(LinkedMemoryObject mobj) {
194                 LinkedMemoryObject tobj = mobj;
195                 int length = 0;
196                 do {
197                         ++length;
198                         tobj = tobj.getNext();
199                 } while (tobj != null && tobj != mobj);
200                 return length;
201         }
202 
203         /**
204          *  Calculate length of array of linear or circular linked lists.
205          *
206          *  @param mobjs array containting heads of lists
207          *  @return length of all lists
208          */
209         public static int getListsLength(LinkedMemoryObject[] mobjs) {
210                 int length = 0;
211                 for (int i = 0; i < mobjs.length; ++i) {
212                         LinkedMemoryObject mobj = mobjs[i];
213                         if (mobj != null)
214                                 length += getListLength(mobj);
215                 }
216                 return length;
217         }
218 
219         /**
220          *  Calculate size of all objects in linear or circular linked list.
221          *
222          *  @param mobj head of list
223          *  @return size of list
224          */
225         public static long getListSize(LinkedMemoryObject mobj) {
226                 LinkedMemoryObject tobj = mobj;
227                 long size = 0;
228                 do {
229                         size += tobj.getSize();
230                         tobj = tobj.getNext();
231                 } while (tobj != null && tobj != mobj);
232                 return size;
233         }
234 
235         /**
236          *  Calculate size of array of linear or circular linked lists.
237          *
238          *  @param mobjs array containting heads of lists
239          *  @return size of all lists
240          */
241         public static long getListsSize(LinkedMemoryObject[] mobjs) {
242                 long size = 0;
243                 for (int i = 0; i < mobjs.length; ++i) {
244                         LinkedMemoryObject mobj = mobjs[i];
245                         if (mobj != null)
246                                 size += getListSize(mobj);
247                 }
248                 return size;
249         }
250 
251         /**
252          *  Create singly linked linear list of objects of fixed size.
253          *
254          *  @param depth length of list
255          *  @param size size of each object
256          *  @return head of created list or null if depth = 0
257          */
258         public static LinkedMemoryObject makeLinearList(int depth, int size) {
259                 LinkedMemoryObject mobj = null;
260                 for (int i = 0; i < depth; ++i)
261                         mobj = new LinkedMemoryObject(size, mobj);
262                 return mobj;
263         }
264 
265         /**
266          *  Create singly linked linear list of objects of varying size.
267          *
268          *  @param depth length of list
269          *  @param size maximum size of each object
270          *  @return head of created list or null if depth = 0
271          */
272         public static LinkedMemoryObject makeRandomLinearList(int depth, int size) {
273                 if (depth == 0)
274                         return null;
275                 LinkedMemoryObject mobj = new LinkedMemoryObject(size);
276                 for (int i = 0; i < depth - 1; ++i)
277                         mobj = new LinkedMemoryObject(LocalRandom.nextInt(size), mobj);
278                 return mobj;
279         }
280 
281         /**
282          *  Create singly linked circular linear list of objects of fixed size.
283          *
284          *  @param depth length of list
285          *  @param size size of each object
286          *  @return head of created list or null if depth = 0
287          */
288         public static LinkedMemoryObject makeCircularList(int depth, int size) {
289                 if (depth == 0)
290                         return null;
291                 LinkedMemoryObject mobj = new LinkedMemoryObject(size);
292                 LinkedMemoryObject tmpobj = mobj;
293                 for (int i = 1; i < depth; i++)
294                         mobj = new LinkedMemoryObject(size, mobj);
295                 tmpobj.setNext(mobj);
296                 return tmpobj;
297         }
298 
299         /**
300          *  Create singly linked circular linear list of objects of varying size.
301          *
302          *  @param depth length of list
303          *  @param size maximum size of each object
304          *  @return head of created list or null if depth = 0
305          */
306         public static LinkedMemoryObject makeRandomCircularList(int depth, int size) {
307                 if (depth == 0)
308                         return null;
309                 LinkedMemoryObject mobj = new LinkedMemoryObject(size);
310                 LinkedMemoryObject tmpobj = mobj;
311                 for (int i = 0; i < depth - 1; i++)
312                         mobj = new LinkedMemoryObject(LocalRandom.nextInt(size), mobj);
313                 tmpobj.setNext(mobj);
314                 return tmpobj;
315         }
316 
317         /**
318          * Create new nonbranchy binary tree.
319          *
320          * Each node in the tree except leaves always has left son. A node
321          * will have right son with probability branchiness.
322          *
323          * @param numberOfNodes number of nodes
324          * @param branchiness branchiness
325          * @param size size of each node
326          * @return root of created tree
327          */
328         public static LinkedMemoryObject makeNonbranchyTree(int numberOfNodes, float branchiness, int size) {
329                 LinkedMemoryObject root = null;
330                 LinkedMemoryObject current = null;
331                 if (numberOfNodes == 0)
332                         return null;
333                 else if (numberOfNodes == 1)
334                         return new LinkedMemoryObject(size);
335                 else if (numberOfNodes == 2)
336                         return new LinkedMemoryObject(size, makeNonbranchyTree(1, branchiness, size));
337                 else {
338                         if (LocalRandom.nextFloat() < branchiness) {
339                                 int numberOfLeftNodes = LocalRandom.nextInt(1, numberOfNodes - 1);
340                                 int numberOfRightNodes = numberOfNodes - 1 - numberOfLeftNodes;
341                                 return new LinkedMemoryObject(
342                                                 size,
343                                                 makeNonbranchyTree(numberOfLeftNodes, branchiness, size),
344                                                 makeNonbranchyTree(numberOfRightNodes, branchiness, size)
345                                                 );
346                         } else {
347                                 return new LinkedMemoryObject(size, makeNonbranchyTree(numberOfNodes - 1, branchiness, size));
348                         }
349                 }
350         }
351 
352         /**
353          * Create a balanced tree of given height.
354          *
355          * @param height height of the tree
356          * @param size size of each node
357          * @return created tree
358          */
359         public static Tree makeBalancedTree(int height, long size) {
360                 return new Tree(makeBalancedTreeNode(height, size));
361         }
362 
363         /**
364          * Get a number of nodes in balanced tree of given height.
365          *
366          * @param heigh height of the tree
367          * @return number of nodes
368          */
369         public static int balancedTreeNodes(int height) {
370                 if (height == 0)
371                         return 0;
372                 int n = 1;
373                 while (height > 1) {
374                         n *= 2;
375                         height--;
376                 }
377                 return n * 2 - 1;
378         }
379 
380         /**
381          * Get approximate memory size occupied by balanced tree
382          * of given height and given node size.
383          *
384          * @param height height of the tree
385          * @param nodeSize size of each node
386          * @return memory size
387          */
388         public static long balancedTreeSize(int height, long nodeSize) {
389                 return balancedTreeNodes(height) * nodeSize;
390         }
391 
392         /**
393          * Get a height of balanced tree with given number of nodes.
394          *
395          * @param nodes number of nodes
396          * @return height of the tree
397          */
398         public static int balancedTreeHeightFromNodes(int nodes) {
399                 if (nodes == 0)
400                         return 0;
401                 int h = 1;
402                 long n = 1;
403                 while (n + n - 1 <= nodes) {
404                         n = n + n;
405                         h = h + 1;
406                 }
407                 return h - 1;
408         }
409 
410         /**
411          * Get approximate height of balanced tree which will occupy
412          * given memory with given node size.
413          *
414          * @param memory memory size
415          * @param nodeSize size of each node
416          * @return approximate height of the tree
417          */
418         public static int balancedTreeHeightFromMemory(long memory, long nodeSize) {
419                 return balancedTreeHeightFromNodes((int) (memory / nodeSize));
420         }
421 
422         /**
423          * Create balanced tree of given height and node size.
424          *
425          * @param height height of the tree
426          * @param size size of each node
427          * @return root of created tree
428          */
429         public static TreeNode makeBalancedTreeNode(int height, long size) {
430                 if (height == 0)
431                         return null;
432                 else
433                         return new TreeNode(size, makeBalancedTreeNode(height - 1, size), makeBalancedTreeNode(height - 1, size));
434         }
435 
436         /**
437          * Create balanced tree of given height and node size.
438          *
439          * @param height height of the tree
440          * @param size size of each node
441          * @return root of created tree
442          */
443         public static TreeNode makeBalancedTreeNode(int height, long size, GarbageProducer gp) {
444                 if (height == 0)
445                         return null;
446                 else
447                         return new TreeNode(size, gp, makeBalancedTreeNode(height - 1, size), makeBalancedTreeNode(height - 1, size));
448         }
449 
450         /**
451          * Determine if given tree is a balanced tree.
452          *
453          * @param tree tree
454          * @return true if tree is balanced
455          */
456         public static boolean isBalancedTree(TreeNode tree) {
457                 return
458                         tree.getActualHeight() == tree.getHeight() &&
459                         tree.getShortestPath() == tree.getHeight();
460         }
461 
462         /**
463          *  Fill an array of MemoryObject's with new objects of given size.
464          *
465          *  @param array array
466          *  @param count number of objects to create
467          *  @param size size of each object
468          */
469         public static void fillArray(MemoryObject[] array, int count, int size) {
470                 for (int i = 0; i < count; ++i)
471                         array[i] = new MemoryObject(size);
472         }
473 
474         /**
475          *  Fill an array of MemoryObject's with new objects of random size.
476          *
477          *  @param array array
478          *  @param count number of objects to create
479          *  @param size maximum size of each object
480          */
481         public static void fillArrayRandom(MemoryObject[] array, int count, int size) {
482                 for (int i = 0; i < count; ++i)
483                         array[i] = new MemoryObject(LocalRandom.nextInt(size));
484         }
485 
486         /**
487          *  Fill an array of MemoryObject's with new objects of random size.
488          *
489          *  @param array array
490          *  @param count number of objects to create
491          *  @param size maximum size of each object
492          */
493         public static void fillArrayRandom(LinkedMemoryObject[] array, int count, int size) {
494                 for (int i = 0; i < count; ++i)
495                         array[i] = new LinkedMemoryObject(LocalRandom.nextInt(size));
496         }
497 
498         public static void dumpStatistics(PrintStream out) {
499                 out.println(Runtime.getRuntime().freeMemory());
500                 out.flush();
501         }
502 
503         public static void dumpStatistics(Log log) {
504                 log.info(Runtime.getRuntime().freeMemory());
505         }
506 
507         public static void dumpStatistics() {
508                 dumpStatistics(System.out);
509         }
510 }