1 package org.unicode.cldr.util; 2 3 import java.text.CharacterIterator; 4 import java.text.StringCharacterIterator; 5 6 public class MemoryHelper { 7 8 /** 9 * Get the amount of memory still available for us to allocate, 10 * including not only freeMemory but also maxMemory - totalMemory 11 * 12 * https://docs.oracle.com/en/java/javase/17/docs/api/java.base/java/lang/Runtime.html#freeMemory() 13 * 14 * Generally: freeMemory <= totalMemory <= maxMemory 15 * 16 * @param callerId a string identifying the caller, used in log if verbose 17 * @param verbose if true, log the stats 18 * @return the available memory, in bytes 19 */ availableMemory(String callerId, boolean verbose)20 public static synchronized long availableMemory(String callerId, boolean verbose) { 21 final Runtime r = Runtime.getRuntime(); 22 long freeMem = r.freeMemory(); 23 long maxMem = r.maxMemory(); 24 long totalMem = r.totalMemory(); 25 if (freeMem > totalMem || totalMem > maxMem) { 26 log(callerId, "Values returned by Runtime violate assumptions!"); 27 verbose = true; 28 } 29 long availMem = freeMem + maxMem - totalMem; 30 if (verbose) { 31 log(callerId, "Available memory: " + humanReadableByteCountSI(availMem) + 32 "; free: " + humanReadableByteCountSI(freeMem) + 33 "; max: " + humanReadableByteCountSI(maxMem) + 34 "; total: " + humanReadableByteCountSI(totalMem)); 35 } 36 return availMem; 37 } 38 log(String callerId, String message)39 private static void log(String callerId, String message) { 40 System.out.println("MemoryHelper[" + callerId + "]: " + message); 41 } 42 43 /** 44 * Convert a byte count to a human readable string 45 * Use SI (1 k = 1,000), not Binary (1 K = 1,024) 46 * 47 * @param bytes the number such as 1234567890 48 * @return the formatted string such as "1.2 GB" 49 * 50 * Source: https://programming.guide/java/formatting-byte-size-to-human-readable-format.html 51 */ humanReadableByteCountSI(long bytes)52 public static String humanReadableByteCountSI(long bytes) { 53 if (-1000 < bytes && bytes < 1000) { 54 return bytes + " B"; 55 } 56 CharacterIterator ci = new StringCharacterIterator("kMGTPE"); 57 while (bytes <= -999_950 || bytes >= 999_950) { 58 bytes /= 1000; 59 ci.next(); 60 } 61 return String.format("%.1f %cB", bytes / 1000.0, ci.current()); 62 } 63 } 64