jvm.option是一些程序里边的java的配置参数的一个集合,不同的应用都会定义自己的jvm.options用来控制一些jvm的参数
以下,以elasticsearch为例,来说明它是如何加载的
elasticsearch的jvm.options的文件内容如下:
## JVM configuration
################################################################
## IMPORTANT: JVM heap size
################################################################
##
## You should always set the min and max JVM heap
## size to the same value. For example, to set
## the heap to 4 GB, set:
##
## -Xms4g
## -Xmx4g
##
## See https://www.elastic.co/guide/en/elasticsearch/reference/current/heap-size.html
## for more information
##
################################################################
# Xms represents the initial size of total heap space
# Xmx represents the maximum size of total heap space
-Xms${heap.min}
-Xmx${heap.max}
################################################################
## Expert settings
################################################################
##
## All settings below this section are considered
## expert settings. Don't tamper with them unless
## you understand what you are doing
##
################################################################
## GC configuration
-XX:+UseConcMarkSweepGC
-XX:CMSInitiatingOccupancyFraction=75
-XX:+UseCMSInitiatingOccupancyOnly
## optimizations
# pre-touch memory pages used by the JVM during initialization
-XX:+AlwaysPreTouch
## basic
# explicitly set the stack size
-Xss1m
# set to headless, just in case
-Djava.awt.headless=true
# ensure UTF-8 encoding by default (e.g. filenames)
-Dfile.encoding=UTF-8
# use our provided JNA always versus the system one
-Djna.nosys=true
# turn off a JDK optimization that throws away stack traces for common
# exceptions because stack traces are important for debugging
-XX:-OmitStackTraceInFastThrow
# flags to configure Netty
-Dio.netty.noUnsafe=true
-Dio.netty.noKeySetOptimization=true
-Dio.netty.recycler.maxCapacityPerThread=0
# log4j 2
-Dlog4j.shutdownHookEnabled=false
-Dlog4j2.disable.jmx=true
-Djava.io.tmpdir=${ES_TMPDIR}
## heap dumps
# generate a heap dump when an allocation from the Java heap fails
# heap dumps are created in the working directory of the JVM
-XX:+HeapDumpOnOutOfMemoryError
# specify an alternative path for heap dumps; ensure the directory exists and
# has sufficient space
${heap.dump.path}
# specify an alternative path for JVM fatal error logs
${error.file}
## JDK 8 GC logging
8:-XX:+PrintGCDetails
8:-XX:+PrintGCDateStamps
8:-XX:+PrintTenuringDistribution
8:-XX:+PrintGCApplicationStoppedTime
8:-Xloggc:${loggc}
8:-XX:+UseGCLogFileRotation
8:-XX:NumberOfGCLogFiles=32
8:-XX:GCLogFileSize=64m
# JDK 9+ GC logging
9-:-Xlog:gc*,gc+age=trace,safepoint:file=${loggc}:utctime,pid,tags:filecount=32,filesize=64m
# due to internationalization enhancements in JDK 9 Elasticsearch need to set the provider to COMPAT otherwise
# time/date parsing will break in an incompatible way for some date patterns and locals
9-:-Djava.locale.providers=COMPAT
# temporary workaround for C2 bug with JDK 10 on hardware with AVX-512
10-:-XX:UseAVX=2
那在ES里边,是如何设置这个参数的呢
我们手动运行这个java类,会得到如下的结果:
所以,ES提供了一个JvmOptionsParser类,来解析jvm.options里设置的jvm参数,然后在应用启动的时候,把这些参数设置到应用的启动参数里边去,我们也可以参考这个思路,提供一个自己的jvm.options文件,然后写一个Parser类来解析里边的内容,然后设置到jvm的启动参数里边去
以下是ES的JvmOptionsParser类的实现
package org.elasticsearch.tools.launchers;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.Reader;
import java.nio.charset.Charset;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Iterator;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.SortedMap;
import java.util.TreeMap;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.elasticsearch.tools.java_version_checker.JavaVersion;
/**
* Parses JVM options from a file and prints a single line with all JVM options to standard output.
*/
final class JvmOptionsParser {
/**
* The main entry point. The exit code is 0 if the JVM options were successfully parsed, otherwise the exit code is 1. If an improperly
* formatted line is discovered, the line is output to standard error.
*
* @param args the args to the program which should consist of a single option, the path to the JVM options
*/
public static void main(final String[] args) throws IOException {
if (args.length != 1) {
throw new IllegalArgumentException("expected one argument specifying path to jvm.options but was " + Arrays.toString(args));
}
final List<String> jvmOptions = new ArrayList<>();
final SortedMap<Integer, String> invalidLines = new TreeMap<>();
try (InputStream is = Files.newInputStream(Paths.get(args[0]));
Reader reader = new InputStreamReader(is, Charset.forName("UTF-8"));
BufferedReader br = new BufferedReader(reader)) {
parse(
JavaVersion.majorVersion(JavaVersion.CURRENT),
br,
new JvmOptionConsumer() {
@Override
public void accept(final String jvmOption) {
jvmOptions.add(jvmOption);
}
},
new InvalidLineConsumer() {
@Override
public void accept(final int lineNumber, final String line) {
invalidLines.put(lineNumber, line);
}
});
}
if (invalidLines.isEmpty()) {
List<String> ergonomicJvmOptions = JvmErgonomics.choose(jvmOptions);
jvmOptions.addAll(ergonomicJvmOptions);
final String spaceDelimitedJvmOptions = spaceDelimitJvmOptions(jvmOptions);
Launchers.outPrintln(spaceDelimitedJvmOptions);
Launchers.exit(0);
} else {
final String errorMessage = String.format(
Locale.ROOT,
"encountered [%d] error%s parsing [%s]",
invalidLines.size(),
invalidLines.size() == 1 ? "" : "s",
args[0]);
Launchers.errPrintln(errorMessage);
int count = 0;
for (final Map.Entry<Integer, String> entry : invalidLines.entrySet()) {
count++;
final String message = String.format(
Locale.ROOT,
"[%d]: encountered improperly formatted JVM option line [%s] on line number [%d]",
count,
entry.getValue(),
entry.getKey());
Launchers.errPrintln(message);
}
Launchers.exit(1);
}
}
/**
* Callback for valid JVM options.
*/
interface JvmOptionConsumer {
/**
* Invoked when a line in the JVM options file matches the specified syntax and the specified major version.
* @param jvmOption the matching JVM option
*/
void accept(String jvmOption);
}
/**
* Callback for invalid lines in the JVM options.
*/
interface InvalidLineConsumer {
/**
* Invoked when a line in the JVM options does not match the specified syntax.
*/
void accept(int lineNumber, String line);
}
private static final Pattern PATTERN = Pattern.compile("((?<start>\d+)(?<range>-)?(?<end>\d+)?:)?(?<option>-.*)$");
/**
* Parse the line-delimited JVM options from the specified buffered reader for the specified Java major version.
* Valid JVM options are:
* <ul>
* <li>
* a line starting with a dash is treated as a JVM option that applies to all versions
* </li>
* <li>
* a line starting with a number followed by a colon is treated as a JVM option that applies to the matching Java major version
* only
* </li>
* <li>
* a line starting with a number followed by a dash followed by a colon is treated as a JVM option that applies to the matching
* Java specified major version and all larger Java major versions
* </li>
* <li>
* a line starting with a number followed by a dash followed by a number followed by a colon is treated as a JVM option that
* applies to the specified range of matching Java major versions
* </li>
* </ul>
*
* For example, if the specified Java major version is 8, the following JVM options will be accepted:
* <ul>
* <li>
* {@code -XX:+PrintGCDateStamps}
* </li>
* <li>
* {@code 8:-XX:+PrintGCDateStamps}
* </li>
* <li>
* {@code 8-:-XX:+PrintGCDateStamps}
* </li>
* <li>
* {@code 7-8:-XX:+PrintGCDateStamps}
* </li>
* </ul>
* and the following JVM options will not be accepted:
* <ul>
* <li>
* {@code 9:-Xlog:age*=trace,gc*,safepoint:file=logs/gc.log:utctime,pid,tags:filecount=32,filesize=64m}
* </li>
* <li>
* {@code 9-:-Xlog:age*=trace,gc*,safepoint:file=logs/gc.log:utctime,pid,tags:filecount=32,filesize=64m}
* </li>
* <li>
* {@code 9-10:-Xlog:age*=trace,gc*,safepoint:file=logs/gc.log:utctime,pid,tags:filecount=32,filesize=64m}
* </li>
* </ul>
*
* If the version syntax specified on a line matches the specified JVM options, the JVM option callback will be invoked with the JVM
* option. If the line does not match the specified syntax for the JVM options, the invalid line callback will be invoked with the
* contents of the entire line.
*
* @param javaMajorVersion the Java major version to match JVM options against
* @param br the buffered reader to read line-delimited JVM options from
* @param jvmOptionConsumer the callback that accepts matching JVM options
* @param invalidLineConsumer a callback that accepts invalid JVM options
* @throws IOException if an I/O exception occurs reading from the buffered reader
*/
static void parse(
final int javaMajorVersion,
final BufferedReader br,
final JvmOptionConsumer jvmOptionConsumer,
final InvalidLineConsumer invalidLineConsumer) throws IOException {
int lineNumber = 0;
while (true) {
final String line = br.readLine();
lineNumber++;
if (line == null) {
break;
}
if (line.startsWith("#")) {
// lines beginning with "#" are treated as comments
continue;
}
if (line.matches("\s*")) {
// skip blank lines
continue;
}
final Matcher matcher = PATTERN.matcher(line);
if (matcher.matches()) {
final String start = matcher.group("start");
final String end = matcher.group("end");
if (start == null) {
// no range present, unconditionally apply the JVM option
jvmOptionConsumer.accept(line);
} else {
final int lower;
try {
lower = Integer.parseInt(start);
} catch (final NumberFormatException e) {
invalidLineConsumer.accept(lineNumber, line);
continue;
}
final int upper;
if (matcher.group("range") == null) {
// no range is present, apply the JVM option to the specified major version only
upper = lower;
} else if (end == null) {
// a range of the form \d+- is present, apply the JVM option to all major versions larger than the specified one
upper = Integer.MAX_VALUE;
} else {
// a range of the form \d+-\d+ is present, apply the JVM option to the specified range of major versions
try {
upper = Integer.parseInt(end);
} catch (final NumberFormatException e) {
invalidLineConsumer.accept(lineNumber, line);
continue;
}
if (upper < lower) {
invalidLineConsumer.accept(lineNumber, line);
continue;
}
}
if (lower <= javaMajorVersion && javaMajorVersion <= upper) {
jvmOptionConsumer.accept(matcher.group("option"));
}
}
} else {
invalidLineConsumer.accept(lineNumber, line);
}
}
}
/**
* Delimits the specified JVM options by spaces.
*
* @param jvmOptions the JVM options
* @return a single-line string containing the specified JVM options in the order they appear delimited by spaces
*/
static String spaceDelimitJvmOptions(final List<String> jvmOptions) {
final StringBuilder spaceDelimitedJvmOptionsBuilder = new StringBuilder();
final Iterator<String> it = jvmOptions.iterator();
while (it.hasNext()) {
spaceDelimitedJvmOptionsBuilder.append(it.next());
if (it.hasNext()) {
spaceDelimitedJvmOptionsBuilder.append(" ");
}
}
return spaceDelimitedJvmOptionsBuilder.toString();
}
}