jvm.option是什麼,它是如何加載的

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();
    }

}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章