|  | @@ -53,25 +53,62 @@ final class JvmErgonomics {
 | 
	
		
			
				|  |  |       */
 | 
	
		
			
				|  |  |      static List<String> choose(final List<String> userDefinedJvmOptions) throws InterruptedException, IOException {
 | 
	
		
			
				|  |  |          final List<String> ergonomicChoices = new ArrayList<>();
 | 
	
		
			
				|  |  | -        final Map<String, Optional<String>> finalJvmOptions = finalJvmOptions(userDefinedJvmOptions);
 | 
	
		
			
				|  |  | +        final Map<String, JvmOption> finalJvmOptions = finalJvmOptions(userDefinedJvmOptions);
 | 
	
		
			
				|  |  |          final long heapSize = extractHeapSize(finalJvmOptions);
 | 
	
		
			
				|  |  |          final long maxDirectMemorySize = extractMaxDirectMemorySize(finalJvmOptions);
 | 
	
		
			
				|  |  |          if (maxDirectMemorySize == 0) {
 | 
	
		
			
				|  |  |              ergonomicChoices.add("-XX:MaxDirectMemorySize=" + heapSize / 2);
 | 
	
		
			
				|  |  |          }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +        final boolean tuneG1GCForSmallHeap = tuneG1GCForSmallHeap(heapSize);
 | 
	
		
			
				|  |  | +        final boolean tuneG1GCHeapRegion = tuneG1GCHeapRegion(finalJvmOptions, tuneG1GCForSmallHeap);
 | 
	
		
			
				|  |  | +        final boolean tuneG1GCInitiatingHeapOccupancyPercent = tuneG1GCInitiatingHeapOccupancyPercent(finalJvmOptions);
 | 
	
		
			
				|  |  | +        final int tuneG1GCReservePercent = tuneG1GCReservePercent(finalJvmOptions, tuneG1GCForSmallHeap);
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +        if (tuneG1GCHeapRegion) {
 | 
	
		
			
				|  |  | +            ergonomicChoices.add("-XX:G1HeapRegionSize=4m");
 | 
	
		
			
				|  |  | +        }
 | 
	
		
			
				|  |  | +        if (tuneG1GCInitiatingHeapOccupancyPercent) {
 | 
	
		
			
				|  |  | +            ergonomicChoices.add("-XX:InitiatingHeapOccupancyPercent=30");
 | 
	
		
			
				|  |  | +        }
 | 
	
		
			
				|  |  | +        if (tuneG1GCReservePercent != 0) {
 | 
	
		
			
				|  |  | +            ergonomicChoices.add("-XX:G1ReservePercent=" + tuneG1GCReservePercent);
 | 
	
		
			
				|  |  | +        }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  |          return ergonomicChoices;
 | 
	
		
			
				|  |  |      }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |      private static final Pattern OPTION = Pattern.compile(
 | 
	
		
			
				|  |  | -        "^\\s*\\S+\\s+(?<flag>\\S+)\\s+:?=\\s+(?<value>\\S+)?\\s+\\{[^}]+?\\}\\s+\\{[^}]+}"
 | 
	
		
			
				|  |  | +        "^\\s*\\S+\\s+(?<flag>\\S+)\\s+:?=\\s+(?<value>\\S+)?\\s+\\{[^}]+?\\}\\s+\\{(?<origin>[^}]+)}"
 | 
	
		
			
				|  |  |      );
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -    static Map<String, Optional<String>> finalJvmOptions(final List<String> userDefinedJvmOptions) throws InterruptedException,
 | 
	
		
			
				|  |  | -        IOException {
 | 
	
		
			
				|  |  | +    private static class JvmOption {
 | 
	
		
			
				|  |  | +        private final String value;
 | 
	
		
			
				|  |  | +        private final String origin;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +        JvmOption(String value, String origin) {
 | 
	
		
			
				|  |  | +            this.value = value;
 | 
	
		
			
				|  |  | +            this.origin = origin;
 | 
	
		
			
				|  |  | +        }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +        public Optional<String> getValue() {
 | 
	
		
			
				|  |  | +            return Optional.ofNullable(value);
 | 
	
		
			
				|  |  | +        }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +        public String getMandatoryValue() {
 | 
	
		
			
				|  |  | +            return value;
 | 
	
		
			
				|  |  | +        }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +        public boolean isCommandLineOrigin() {
 | 
	
		
			
				|  |  | +            return "command line".equals(this.origin);
 | 
	
		
			
				|  |  | +        }
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    static Map<String, JvmOption> finalJvmOptions(final List<String> userDefinedJvmOptions) throws InterruptedException, IOException {
 | 
	
		
			
				|  |  |          return flagsFinal(userDefinedJvmOptions).stream()
 | 
	
		
			
				|  |  |              .map(OPTION::matcher)
 | 
	
		
			
				|  |  |              .filter(Matcher::matches)
 | 
	
		
			
				|  |  | -            .collect(Collectors.toUnmodifiableMap(m -> m.group("flag"), m -> Optional.ofNullable(m.group("value"))));
 | 
	
		
			
				|  |  | +            .collect(Collectors.toUnmodifiableMap(m -> m.group("flag"), m -> new JvmOption(m.group("value"), m.group("origin"))));
 | 
	
		
			
				|  |  |      }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |      private static List<String> flagsFinal(final List<String> userDefinedJvmOptions) throws InterruptedException, IOException {
 | 
	
	
		
			
				|  | @@ -116,12 +153,42 @@ final class JvmErgonomics {
 | 
	
		
			
				|  |  |      }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |      // package private for testing
 | 
	
		
			
				|  |  | -    static Long extractHeapSize(final Map<String, Optional<String>> finalJvmOptions) {
 | 
	
		
			
				|  |  | -        return Long.parseLong(finalJvmOptions.get("MaxHeapSize").get());
 | 
	
		
			
				|  |  | +    static Long extractHeapSize(final Map<String, JvmOption> finalJvmOptions) {
 | 
	
		
			
				|  |  | +        return Long.parseLong(finalJvmOptions.get("MaxHeapSize").getMandatoryValue());
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    static long extractMaxDirectMemorySize(final Map<String, JvmOption> finalJvmOptions) {
 | 
	
		
			
				|  |  | +        return Long.parseLong(finalJvmOptions.get("MaxDirectMemorySize").getMandatoryValue());
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    // Tune G1GC options for heaps < 8GB
 | 
	
		
			
				|  |  | +    static boolean tuneG1GCForSmallHeap(final long heapSize) {
 | 
	
		
			
				|  |  | +        return heapSize < 8L << 30;
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    static boolean tuneG1GCHeapRegion(final Map<String, JvmOption> finalJvmOptions, final boolean tuneG1GCForSmallHeap) {
 | 
	
		
			
				|  |  | +        JvmOption g1GCHeapRegion = finalJvmOptions.get("G1HeapRegionSize");
 | 
	
		
			
				|  |  | +        JvmOption g1GC = finalJvmOptions.get("UseG1GC");
 | 
	
		
			
				|  |  | +        return (tuneG1GCForSmallHeap && g1GC.getMandatoryValue().equals("true") && g1GCHeapRegion.isCommandLineOrigin() == false);
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    static int tuneG1GCReservePercent(final Map<String, JvmOption> finalJvmOptions, final boolean tuneG1GCForSmallHeap) {
 | 
	
		
			
				|  |  | +        JvmOption g1GC = finalJvmOptions.get("UseG1GC");
 | 
	
		
			
				|  |  | +        JvmOption g1GCReservePercent = finalJvmOptions.get("G1ReservePercent");
 | 
	
		
			
				|  |  | +        if (g1GC.getMandatoryValue().equals("true")) {
 | 
	
		
			
				|  |  | +            if (g1GCReservePercent.isCommandLineOrigin() == false && tuneG1GCForSmallHeap) {
 | 
	
		
			
				|  |  | +                return 15;
 | 
	
		
			
				|  |  | +            } else if (g1GCReservePercent.isCommandLineOrigin() == false && tuneG1GCForSmallHeap == false) {
 | 
	
		
			
				|  |  | +                return 25;
 | 
	
		
			
				|  |  | +            }
 | 
	
		
			
				|  |  | +        }
 | 
	
		
			
				|  |  | +        return 0;
 | 
	
		
			
				|  |  |      }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -    static long extractMaxDirectMemorySize(final Map<String, Optional<String>> finalJvmOptions) {
 | 
	
		
			
				|  |  | -        return Long.parseLong(finalJvmOptions.get("MaxDirectMemorySize").get());
 | 
	
		
			
				|  |  | +    static boolean tuneG1GCInitiatingHeapOccupancyPercent(final Map<String, JvmOption> finalJvmOptions) {
 | 
	
		
			
				|  |  | +        JvmOption g1GC = finalJvmOptions.get("UseG1GC");
 | 
	
		
			
				|  |  | +        JvmOption g1GCInitiatingHeapOccupancyPercent = finalJvmOptions.get("InitiatingHeapOccupancyPercent");
 | 
	
		
			
				|  |  | +        return g1GCInitiatingHeapOccupancyPercent.isCommandLineOrigin() == false && g1GC.getMandatoryValue().equals("true");
 | 
	
		
			
				|  |  |      }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |      private static final Pattern SYSTEM_PROPERTY = Pattern.compile("^-D(?<key>[\\w+].*?)=(?<value>.*)$");
 |