Clover icon

Coverage Report

  1. Project Clover database Tue Oct 29 2024 21:36:55 GMT
  2. Package jalview.bin

File MemorySetting.java

 

Coverage histogram

../../img/srcFileCovDistChart7.png
29% of files have more coverage

Code metrics

48
98
10
1
416
275
50
0.51
9.8
10
5

Classes

Class Line # Actions
MemorySetting 40 98 50
0.621794962.2%
 

Contributing tests

This file is covered by 1 test. .

Source view

1    /*
2   
3    private static String ADJUSTMENT_MESSAGE = null;
4    * Jalview - A Sequence Alignment Editor and Viewer ($$Version-Rel$$)
5    * Copyright (C) $$Year-Rel$$ The Jalview Authors
6    *
7    * This file is part of Jalview.
8    *
9    * Jalview is free software: you can redistribute it and/or
10    * modify it under the terms of the GNU General Public License
11    * as published by the Free Software Foundation, either version 3
12    * of the License, or (at your option) any later version.
13    *
14    * Jalview is distributed in the hope that it will be useful, but
15    * WITHOUT ANY WARRANTY; without even the implied warranty
16    * of MERCHANTABILITY or FITNESS FOR A PARTICULAR
17    * PURPOSE. See the GNU General Public License for more details.
18    *
19    * You should have received a copy of the GNU General Public License
20    * along with Jalview. If not, see <http://www.gnu.org/licenses/>.
21    * The Jalview Authors are detailed in the 'AUTHORS' file.
22    */
23    package jalview.bin;
24   
25    import java.util.Locale;
26   
27    import jalview.util.ErrorLog;
28   
29    /**
30    * Methods to decide on appropriate memory setting for Jalview based on two
31    * optionally provided values: jvmmempc - the maximum percentage of total
32    * physical memory to allocate, and jvmmemmax - the maximum absolute amount of
33    * physical memory to allocate. These can be provided as arguments or system
34    * properties. Other considerations such as minimum application requirements and
35    * leaving space for OS are used too.
36    *
37    * @author bsoares
38    *
39    */
 
40    public class MemorySetting
41    {
42    // This must match the value of Arg.JVMMEMPC.getName()
43    public static final String MAX_HEAPSIZE_PERCENT_PROPERTY_NAME = "jvmmempc";
44   
45    // This must match the value of Arg.JVMMEMMAX.getName()
46    public static final String MAX_HEAPSIZE_PROPERTY_NAME = "jvmmemmax";
47   
48    private static final int MAX_HEAPSIZE_PERCENT_DEFAULT = 90; // 90%
49   
50    private static final long GIGABYTE = 1073741824; // 1GB
51   
52    public static final long LEAVE_FREE_MIN_MEMORY = GIGABYTE / 2;
53   
54    public static final long APPLICATION_MIN_MEMORY = GIGABYTE / 2;
55   
56    private static final long MAX_HEAPSIZE_GB_DEFAULT = 32;
57   
58    private static final long NOMEM_MAX_HEAPSIZE_GB_DEFAULT = 8;
59   
60    public static final String NS = "MEMORY";
61   
62    public static final String CUSTOMISED_SETTINGS = NS
63    + "_CUSTOMISED_SETTINGS";
64   
65    public static final String MEMORY_JVMMEMPC = NS + "_"
66    + MAX_HEAPSIZE_PERCENT_PROPERTY_NAME.toUpperCase(Locale.ROOT);
67   
68    public static final String MEMORY_JVMMEMMAX = NS + "_"
69    + MAX_HEAPSIZE_PROPERTY_NAME.toUpperCase(Locale.ROOT);
70   
71    protected static boolean logToClassChecked = false;
72   
73    public static String memorySuffixes = "bkmgt"; // order of the suffixes is
74    // important!
75   
 
76  1 toggle public static long getMemorySetting()
77    {
78  1 return getMemorySetting(null, null);
79    }
80   
 
81  16 toggle public static long getMemorySetting(String jvmmemmaxarg,
82    String jvmmempcarg)
83    {
84  16 return getMemorySetting(jvmmemmaxarg, jvmmempcarg, true, false);
85    }
86   
87    /**
88    * Decide on appropriate memory setting for Jalview based on the two arguments
89    * values: jvmmempc - the maximum percentage of total physical memory to
90    * allocate, and jvmmemmax - the maximum absolute amount of physical memory to
91    * allocate. These can be provided as arguments. If not provided as arguments
92    * (or set as null) system properties will be used instead (if set). The
93    * memory setting returned will be the lower of the two values. If either of
94    * the values are not provided then defaults will be used (jvmmempc=90,
95    * jvmmemmax=32GB). If total physical memory can't be ascertained when
96    * jvmmempc was set or neither jvmmempc nor jvmmemmax were set, then jvmmemmax
97    * defaults to a much safer 8GB. In this case explicitly setting jvmmemmax and
98    * not setting jvmmempc can set a higher memory for Jalview. The calculation
99    * also tries to ensure 0.5GB memory for the OS, but also tries to ensure at
100    * least 0.5GB memory for Jalview (which takes priority over the OS) If there
101    * is less then 0.5GB of physical memory then the total physical memory is
102    * used for Jalview.
103    *
104    * @param jvmmemmaxarg
105    * Maximum value of memory to set. This can be a numeric string
106    * optionally followed by "b", "k", "m", "g", "t" (case insensitive)
107    * to indicate bytes, kilobytes, megabytes, gigabytes, terabytes
108    * respectively. If null a default value of 32G will be used. If null
109    * and either physical memory can't be determined then the default is
110    * 8GB.
111    * @param jvmmempcarg
112    * Max percentage of physical memory to use. Defaults to "90".
113    *
114    * @param useProps
115    * boolean to decide whether to look at System properties.
116    *
117    * @return The amount of memory (in bytes) to allocate to Jalview
118    */
 
119  16 toggle public static long getMemorySetting(String jvmmemmaxarg,
120    String jvmmempcarg, boolean useProps, boolean quiet)
121    {
122    // actual Xmx value-to-be
123  16 long maxMemLong = -1;
124  16 clearAdjustmentMessage();
125   
126    // (absolute) jvmmaxmem setting, start with default
127  16 long memmax = MAX_HEAPSIZE_GB_DEFAULT * GIGABYTE;
128  16 if (jvmmemmaxarg == null && useProps)
129    {
130  1 jvmmemmaxarg = System.getProperty(MAX_HEAPSIZE_PROPERTY_NAME);
131    }
132  16 String jvmmemmax = jvmmemmaxarg;
133  16 if (jvmmemmax != null && jvmmemmax.length() > 0)
134    {
135    // parse the arg
136  15 try
137    {
138  15 memmax = memoryStringToLong(jvmmemmax);
139  15 if (memmax == 0)
140    {
141  0 throw (new NumberFormatException("Not allowing 0"));
142    }
143    } catch (NumberFormatException e)
144    {
145  0 memmax = MAX_HEAPSIZE_GB_DEFAULT * GIGABYTE;
146  0 setAdjustmentMessage("MemorySetting Property '"
147    + MAX_HEAPSIZE_PROPERTY_NAME + "' (" + jvmmemmaxarg
148    + "') badly formatted or 0, using default ("
149    + MAX_HEAPSIZE_GB_DEFAULT + "g).", quiet);
150    }
151   
152    // check at least minimum value (this accounts for negatives too)
153  15 if (memmax < APPLICATION_MIN_MEMORY)
154    {
155  1 memmax = APPLICATION_MIN_MEMORY;
156  1 setAdjustmentMessage("MemorySetting Property '"
157    + MAX_HEAPSIZE_PROPERTY_NAME + "' (" + jvmmemmaxarg
158    + ") too small, using minimum (" + APPLICATION_MIN_MEMORY
159    + ").", quiet);
160    }
161   
162    }
163    else
164    {
165    // no need to warn if no setting
166    // adjustmentMessage("MemorySetting Property '" + maxHeapSizeProperty
167    // + "' not
168    // set.");
169    }
170   
171    // get max percent of physical memory, starting with default
172  16 float percent = MAX_HEAPSIZE_PERCENT_DEFAULT;
173  16 if (jvmmempcarg == null && useProps)
174    {
175  1 jvmmempcarg = System.getProperty(MAX_HEAPSIZE_PERCENT_PROPERTY_NAME);
176    }
177  16 String jvmmempc = jvmmempcarg;
178  16 long mempc = -1;
179  16 try
180    {
181  16 if (jvmmempc != null)
182    {
183  15 int trypercent = Integer.parseInt(jvmmempc);
184  14 if (0 <= trypercent && trypercent <= 100)
185    {
186  14 percent = trypercent;
187    }
188    else
189    {
190  0 setAdjustmentMessage("MemorySetting Property '"
191    + MAX_HEAPSIZE_PERCENT_PROPERTY_NAME
192    + "' should be in range 0..100. Using default " + percent
193    + "%", quiet);
194    }
195    }
196    } catch (NumberFormatException e)
197    {
198  1 setAdjustmentMessage("MemorySetting property '"
199    + MAX_HEAPSIZE_PERCENT_PROPERTY_NAME + "' (" + jvmmempcarg
200    + ") badly formatted", quiet);
201    }
202   
203    // catch everything in case of no com.sun.management.OperatingSystemMXBean
204  16 boolean memoryPercentError = false;
205  16 try
206    {
207  16 long physicalMem = GetMemory.getPhysicalMemory();
208  16 if (physicalMem > APPLICATION_MIN_MEMORY)
209    {
210    // try and set at least applicationMinMemory and thereafter ensure
211    // leaveFreeMinMemory is left for the OS
212   
213  16 mempc = (long) ((physicalMem / 100F) * percent);
214   
215    // check for memory left for OS
216  16 boolean reducedmempc = false;
217  16 if (physicalMem - mempc < LEAVE_FREE_MIN_MEMORY)
218    {
219  12 mempc = physicalMem - LEAVE_FREE_MIN_MEMORY;
220  12 reducedmempc = true;
221  12 setAdjustmentMessage("MemorySetting Property '"
222    + MAX_HEAPSIZE_PERCENT_PROPERTY_NAME + "' (" + jvmmempcarg
223    + ") too large. Leaving free space for OS and reducing to ("
224    + mempc + ").", quiet);
225    }
226   
227    // check for minimum application memsize
228  16 if (mempc < APPLICATION_MIN_MEMORY)
229    {
230  0 if (reducedmempc)
231    {
232  0 setAdjustmentMessage("Reduced MemorySetting (" + mempc
233    + ") too small. Increasing to application minimum ("
234    + APPLICATION_MIN_MEMORY + ").", quiet);
235    }
236    else
237    {
238  0 setAdjustmentMessage("MemorySetting Property '"
239    + MAX_HEAPSIZE_PERCENT_PROPERTY_NAME + "' ("
240    + jvmmempcarg + ") too small. Using minimum ("
241    + APPLICATION_MIN_MEMORY + ").", quiet);
242    }
243  0 mempc = APPLICATION_MIN_MEMORY;
244    }
245    }
246    else
247    {
248    // not enough memory for application, just try and grab what we can!
249  0 mempc = physicalMem;
250  0 setAdjustmentMessage(
251    "Not enough physical memory for application. Ignoring MemorySetting Property '"
252    + MAX_HEAPSIZE_PERCENT_PROPERTY_NAME + "' ("
253    + jvmmempcarg
254    + "). Using maximum memory available ("
255    + physicalMem + ").",
256    quiet);
257    }
258   
259    } catch (Throwable t)
260    {
261  0 memoryPercentError = true;
262  0 setAdjustmentMessage(
263    "Problem calling GetMemory.getPhysicalMemory(). Likely to be problem with com.sun.management.OperatingSystemMXBean",
264    quiet);
265  0 t.printStackTrace();
266    }
267   
268    // In the case of an error reading the percentage of physical memory (when
269    // jvmmempc was set OR neither jvmmempc nor jvmmemmax were set), let's cap
270    // maxMemLong to 8GB
271  16 if (memoryPercentError && mempc == -1
272    && !(jvmmempcarg == null && jvmmemmaxarg != null) // the same as
273    // (jvmmempcarg !=
274    // null ||
275    // (jvmmempcarg ==
276    // null &&
277    // jvmmemmaxarg
278    // == null))
279    && memmax > NOMEM_MAX_HEAPSIZE_GB_DEFAULT * GIGABYTE)
280    {
281  0 setAdjustmentMessage(
282    "Capping maximum memory to " + NOMEM_MAX_HEAPSIZE_GB_DEFAULT
283    + "g due to failure to read physical memory size.",
284    quiet);
285  0 memmax = NOMEM_MAX_HEAPSIZE_GB_DEFAULT * GIGABYTE;
286    }
287   
288  16 if (mempc == -1) // percentage memory not set
289    {
290  0 maxMemLong = memmax;
291    }
292    else
293    {
294  16 maxMemLong = Math.min(mempc, memmax);
295    }
296   
297  16 return maxMemLong;
298    }
299   
 
300  15 toggle public static boolean isValidMemoryString(String text)
301    {
302  15 if (text.length() > 0)
303    {
304  15 char lastChar = text.charAt(text.length() - 1);
305  15 char[] otherChars = text.substring(0, text.length() - 1)
306    .toCharArray();
307  15 for (char c : otherChars)
308    {
309  59 if (c < '0' || c > '9')
310    {
311  0 return false;
312    }
313    }
314  15 if ((lastChar < '0' || lastChar > '9') && memorySuffixes
315    .indexOf(Character.toLowerCase(lastChar)) == -1)
316    {
317  0 return false;
318    }
319    }
320  15 return true;
321    }
322   
 
323  15 toggle public static long memoryStringToLong(String memString)
324    throws NumberFormatException
325    {
326  15 if (!isValidMemoryString(memString)) // not valid
327    {
328  0 throw (new NumberFormatException("Not a valid memory string"));
329    }
330  15 char suffix = Character
331    .toLowerCase(memString.charAt(memString.length() - 1));
332  15 if ('0' <= suffix && suffix <= '9') // no suffix
333    {
334  1 return Long.valueOf(memString);
335    }
336  14 if (memorySuffixes.indexOf(suffix) == -1) // suffix is unknown
337    {
338  0 return -1;
339    }
340   
341  14 long multiplier = (long) Math.pow(2,
342    memorySuffixes.indexOf(suffix) * 10); // note order of suffixes in
343    // memorySuffixes important
344    // here!
345    // parse the arg. NumberFormatExceptions passed on to calling method
346  14 long mem = Long
347    .parseLong(memString.substring(0, memString.length() - 1));
348  14 if (mem == 0)
349    {
350  0 return 0;
351    }
352   
353    // apply multiplier only if result is not too big (i.e. bigger than a long)
354  14 if (Long.MAX_VALUE / mem > multiplier)
355    {
356  14 return multiplier * mem;
357    }
358    else
359    {
360    // number too big for a Long. Limit to Long.MAX_VALUE
361  0 ErrorLog.outPrintln("Memory parsing of '" + memString
362    + "' produces number too big. Limiting to Long.MAX_VALUE="
363    + Long.MAX_VALUE);
364  0 return Long.MAX_VALUE;
365    }
366    }
367   
 
368  0 toggle public static String memoryLongToString(long mem)
369    {
370  0 return memoryLongToString(mem, "%.3f");
371    }
372   
 
373  0 toggle public static String memoryLongToString(long mem, String format)
374    {
375  0 int exponent = 0;
376  0 float num = mem;
377  0 char suffix = 'b';
378   
379  0 for (int i = 0; i < memorySuffixes.length(); i++)
380    {
381  0 char s = Character.toUpperCase(memorySuffixes.charAt(i));
382  0 if (mem < (long) Math.pow(2, exponent + 10)
383    || i == memorySuffixes.length() - 1) // last suffix
384    {
385  0 suffix = s;
386  0 num = (float) (mem / Math.pow(2, exponent));
387  0 break;
388    }
389  0 exponent += 10;
390    }
391   
392  0 return String.format(format, num) + suffix;
393    }
394   
395    private static String ADJUSTMENT_MESSAGE = null;
396   
 
397  14 toggle private static void setAdjustmentMessage(String reason, boolean quiet)
398    {
399  14 ADJUSTMENT_MESSAGE = reason;
400  14 if (!quiet)
401    {
402  14 ErrorLog.outPrintln(reason);
403    }
404    }
405   
 
406  16 toggle public static void clearAdjustmentMessage()
407    {
408  16 ADJUSTMENT_MESSAGE = null;
409    }
410   
 
411  0 toggle public static String getAdjustmentMessage()
412    {
413  0 return ADJUSTMENT_MESSAGE;
414    }
415   
416    }