117be25aab7ac8f49ea880293d8c0ae9e95e9356
[jalview.git] / getdown / src / getdown / core / src / main / java / jalview / bin / MemorySetting.java
1 package jalview.bin;
2
3 /**
4  * Methods to decide on appropriate memory setting for Jalview based on two
5  * optionally provided values: jvmmempc - the maximum percentage of total
6  * physical memory to allocate, and jvmmemmax - the maximum absolute amount of
7  * physical memory to allocate. These can be provided as arguments or system
8  * properties. Other considerations such as minimum application requirements and
9  * leaving space for OS are used too.
10  * 
11  * @author bsoares
12  *
13  */
14 public class MemorySetting
15 {
16   public static final long leaveFreeMinMemory = 536870912; // 0.5 GB
17
18   public static final long applicationMinMemory = 536870912; // 0.5 GB
19
20   private final static int maxHeapSizePerCentDefault = 90;
21
22   public final static String maxHeapSizePerCentPropertyName = "jvmmempc";
23
24   private final static long maxHeapSizeDefault = 34359738368L; // 32GB
25
26   private final static long noMemMaxHeapSizeDefault = 8589934592L; // 8GB
27
28   public final static String maxHeapSizePropertyName = "jvmmemmax";
29
30   protected static boolean logToClassChecked = false;
31
32   public static long getMemorySetting()
33   {
34     return getMemorySetting(null, null);
35   }
36
37   /**
38    * Decide on appropriate memory setting for Jalview based on the two arguments
39    * values: jvmmempc - the maximum percentage of total physical memory to
40    * allocate, and jvmmemmax - the maximum absolute amount of physical memory to
41    * allocate. These can be provided as arguments. If not provided as arguments
42    * (or set as null) system properties will be used instead (if set). The memory
43    * setting returned will be the lower of the two values. If either of the values
44    * are not provided then defaults will be used (jvmmempc=90, jvmmemmax=32GB). If
45    * total physical memory can't be ascertained when jvmmempc was set or neither
46    * jvmmempc nor jvmmemmax were set, then jvmmemmax defaults to a much safer 8GB.
47    * In this case explicitly setting jvmmemmax and not setting jvmmempc can set a
48    * higher memory for Jalview. The calculation also tries to ensure 0.5GB memory
49    * for the OS, but also tries to ensure at least 0.5GB memory for Jalview (which
50    * takes priority over the OS) If there is less then 0.5GB of physical memory
51    * then the total physical memory is used for Jalview.
52    * 
53    * @param jvmmemmaxarg
54    *                       Maximum value of memory to set. This can be a numeric
55    *                       string optionally followed by "b", "k", "m", "g", "t"
56    *                       (case insensitive) to indicate bytes, kilobytes,
57    *                       megabytes, gigabytes, terabytes respectively. If null a
58    *                       default value of 32G will be used. If null and either
59    *                       physical memory can't be determined then the default is
60    *                       8GB.
61    * @param jvmmempcarg
62    *                       Max percentage of physical memory to use. Defaults to
63    *                       "90".
64    * 
65    * @return The amount of memory (in bytes) to allocate to Jalview
66    */
67   public static long getMemorySetting(String jvmmemmaxarg,
68           String jvmmempcarg)
69   {
70     // actual Xmx value-to-be
71     long maxMemLong = -1;
72
73     // (absolute) jvmmaxmem setting, start with default
74     long memmax = maxHeapSizeDefault;
75     if (jvmmemmaxarg == null)
76     {
77       jvmmemmaxarg = System.getProperty(maxHeapSizePropertyName);
78     }
79     String jvmmemmax = jvmmemmaxarg;
80     if (jvmmemmax != null && jvmmemmax.length() > 0)
81     {
82       long multiplier = 1;
83       switch (jvmmemmax.toLowerCase().substring(jvmmemmax.length() - 1))
84       {
85       case "t":
86         multiplier = 1099511627776L; // 2^40
87         jvmmemmax = jvmmemmax.substring(0, jvmmemmax.length() - 1);
88         break;
89       case "g":
90         multiplier = 1073741824; // 2^30
91         jvmmemmax = jvmmemmax.substring(0, jvmmemmax.length() - 1);
92         break;
93       case "m":
94         multiplier = 1048576; // 2^20
95         jvmmemmax = jvmmemmax.substring(0, jvmmemmax.length() - 1);
96         break;
97       case "k":
98         multiplier = 1024; // 2^10
99         jvmmemmax = jvmmemmax.substring(0, jvmmemmax.length() - 1);
100         break;
101       case "b":
102         multiplier = 1; // 2^0
103         jvmmemmax = jvmmemmax.substring(0, jvmmemmax.length() - 1);
104         break;
105       default:
106         break;
107       }
108
109       // parse the arg
110       try
111       {
112         memmax = Long.parseLong(jvmmemmax);
113       } catch (NumberFormatException e)
114       {
115         memmax = maxHeapSizeDefault;
116         System.out.println("MemorySetting Property '"
117                 + maxHeapSizePropertyName
118                 + "' ("
119                 + jvmmemmaxarg + "') badly formatted, using default ("
120                 + memmax + ").");
121       }
122
123       // apply multiplier if not too big (i.e. bigger than a long)
124       if (Long.MAX_VALUE / memmax < multiplier)
125       {
126         memmax = maxHeapSizeDefault;
127         System.out.println(
128                 "MemorySetting Property '" + maxHeapSizePropertyName + "' ("
129                         + jvmmemmaxarg
130                         + ") too big, using default (" + memmax + ").");
131       }
132       else
133       {
134         memmax = multiplier * memmax;
135       }
136
137       // check at least minimum value (this accounts for negatives too)
138       if (memmax < applicationMinMemory)
139       {
140         memmax = applicationMinMemory;
141         System.out.println(
142                 "MemorySetting Property '" + maxHeapSizePropertyName + "' ("
143                         + jvmmemmaxarg
144                         + ") too small, using minimum ("
145                         + applicationMinMemory + ").");
146       }
147
148     }
149     else
150     {
151       // no need to warn if no setting
152       // System.out.println("MemorySetting Property '" + maxHeapSizeProperty
153       // + "' not
154       // set.");
155     }
156
157     // get max percent of physical memory, starting with default
158     float percent = maxHeapSizePerCentDefault;
159     if (jvmmempcarg == null)
160     {
161       jvmmempcarg = System.getProperty(maxHeapSizePerCentPropertyName);
162     }
163     String jvmmempc = jvmmempcarg;
164     long mempc = -1;
165     try
166     {
167       if (jvmmempc != null)
168       {
169         float trypercent = Float.parseFloat(jvmmempc);
170         if (0 < trypercent && trypercent <= 100f)
171         {
172           percent = trypercent;
173         }
174         else
175         {
176           System.out.println(
177                   "MemorySetting Property '"
178                           + maxHeapSizePerCentPropertyName
179                           + "' should be in range 1..100. Using default "
180                           + percent + "%");
181         }
182       }
183     } catch (NumberFormatException e)
184     {
185       System.out.println(
186               "MemorySetting property '" + maxHeapSizePerCentPropertyName
187                       + "' (" + jvmmempcarg + ") badly formatted");
188     }
189
190     // catch everything in case of no com.sun.management.OperatingSystemMXBean
191     boolean memoryPercentError = false;
192     try
193     {
194       long physicalMem = GetMemory.getPhysicalMemory();
195       if (physicalMem > applicationMinMemory)
196       {
197         // try and set at least applicationMinMemory and thereafter ensure
198         // leaveFreeMinMemory is left for the OS
199
200         mempc = (long) ((physicalMem / 100F) * percent);
201
202         // check for memory left for OS
203         boolean reducedmempc = false;
204         if (physicalMem - mempc < leaveFreeMinMemory)
205         {
206           mempc = physicalMem - leaveFreeMinMemory;
207           reducedmempc = true;
208           System.out.println("MemorySetting Property '"
209                   + maxHeapSizePerCentPropertyName + "' (" + jvmmempcarg
210                   + ") too large. Leaving free space for OS and reducing to ("
211                   + mempc + ").");
212         }
213
214         // check for minimum application memsize
215         if (mempc < applicationMinMemory)
216         {
217           if (reducedmempc)
218           {
219             System.out.println("Reduced MemorySetting (" + mempc
220                     + ") too small. Increasing to application minimum ("
221                     + applicationMinMemory + ").");
222           }
223           else
224           {
225             System.out.println("MemorySetting Property '"
226                     + maxHeapSizePerCentPropertyName + "' (" + jvmmempcarg
227                     + ") too small. Using minimum (" + applicationMinMemory
228                     + ").");
229           }
230           mempc = applicationMinMemory;
231         }
232       }
233       else
234       {
235         // not enough memory for application, just try and grab what we can!
236         mempc = physicalMem;
237         System.out.println(
238                 "Not enough physical memory for application. Ignoring MemorySetting Property '"
239                         + maxHeapSizePerCentPropertyName + "' ("
240                         + jvmmempcarg
241                         + "). Using maximum memory available ("
242                         + physicalMem + ").");
243       }
244
245     } catch (Throwable t)
246     {
247       memoryPercentError = true;
248       System.out.println(
249               "Problem calling GetMemory.getPhysicalMemory(). Likely to be problem with com.sun.management.OperatingSystemMXBean");
250       t.printStackTrace();
251     }
252
253     // In the case of an error reading the percentage of physical memory (when
254     // jvmmempc was set OR neither jvmmempc nor jvmmemmax were set), let's cap
255     // maxMemLong to 8GB
256     if (memoryPercentError && mempc == -1
257     // && (jvmmempcarg != null || (jvmmempcarg == null && jvmmemmaxarg == null))
258     // the above is easier to understand but simplified to
259             && !(jvmmempcarg == null && jvmmemmaxarg != null)
260             && memmax > noMemMaxHeapSizeDefault)
261     {
262       System.out.println(
263               "Capping maximum memory to "
264                       + (noMemMaxHeapSizeDefault + 536870912) / 1073741824 // this is the nearest integer GB for noMemMaxHeapSizeDefault
265                       + "GB due to failure to read physical memory size.");
266       memmax = noMemMaxHeapSizeDefault;
267     }
268
269     if (mempc == -1) // percentage memory not set
270     {
271       maxMemLong = memmax;
272     }
273     else
274     {
275       maxMemLong = Math.min(mempc, memmax);
276     }
277
278     return maxMemLong;
279   }
280
281 }