at.ac.univie.isc.asio.tool.JvmOptions.java Source code

Java tutorial

Introduction

Here is the source code for at.ac.univie.isc.asio.tool.JvmOptions.java

Source

/*
 * #%L
 * asio cli
 * %%
 * Copyright (C) 2013 - 2015 Research Group Scientific Computing, University of Vienna
 * %%
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * 
 *      http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 * #L%
 */
package at.ac.univie.isc.asio.tool;

import com.google.common.base.Joiner;
import com.google.common.base.Preconditions;
import com.google.common.collect.ImmutableList;
import com.google.common.math.DoubleMath;
import org.slf4j.Logger;

import java.io.File;
import java.math.RoundingMode;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Collection;

import static org.slf4j.LoggerFactory.getLogger;

/**
 * Build dynamic JVM startup parameters, taking into account information from the current runtime.
 */
public final class JvmOptions {
    private static final Logger log = getLogger(JvmOptions.class);

    public static final int MEBI_BYTE = 1024 * 1024;

    private static final String MODE = "-server";
    private static final String DNS_CACHE_TTL = "-Dsun.net.inetaddr.ttl=60";
    private static final String[] FORCE_IPV4 = { "-Djava.net.preferIPv4Stack=true",
            "-Djava.net.preferIPv6Addresses=false" };

    private final JvmSystemInfo system = JvmSystemInfo.create();

    // toggles
    private boolean forceIPv4 = false;
    private double relativeHeapSize = -1.0;
    private String home = asDirectoryPath(Paths.get(System.getProperty("java.io.tmpdir")));

    public static JvmOptions create() {
        return new JvmOptions();
    }

    private JvmOptions() {
    }

    /**
     * Inspect environment and determine JVM options for current settings.
     *
     * @return a collection of JVM startup arguments
     */
    public Collection<String> collect() {
        log.debug("using <{}> as home directory", home);
        final ImmutableList.Builder<String> args = ImmutableList.builder();
        args.add(MODE, DNS_CACHE_TTL);
        args.add(logging());
        if (forceIPv4) {
            args.add(FORCE_IPV4);
        }
        args.add(garbageCollector());
        args.add(heap());
        return args.build();
    }

    private static final Joiner WHITESPACE_JOINER = Joiner.on(' ');

    /**
     * @return the collected jvm arguments as a command line string (separated by a single space)
     */
    @Override
    public String toString() {
        return WHITESPACE_JOINER.join(collect());
    }

    /**
     * turn a raw path text into a folder path, with platform dependent trailing separator
     */
    private String asDirectoryPath(final Path path) {
        String result = path.normalize().toAbsolutePath().toString();
        if (!result.endsWith(File.separator)) {
            result += File.separator;
        }
        return result;
    }

    private String[] logging() {
        return new String[] {
                // heap logging
                "-XX:+HeapDumpOnOutOfMemoryError", "-XX:HeapDumpPath=" + home,
                // gc logging
                "-verbose:gc", "-XX:+PrintGCDetails", "-XX:+PrintGCDateStamps", "-Xloggc:" + home + "gc.log",
                "-XX:+UseGCLogFileRotation", "-XX:NumberOfGCLogFiles=4", "-XX:GCLogFileSize=10M" };
    }

    private String[] garbageCollector() {
        return new String[] { "-XX:+UseG1GC", "-XX:MaxGCPauseMillis=250", "-XX:+ScavengeBeforeFullGC" };
    }

    private String[] heap() {
        if (relativeHeapSize < 0) {
            return new String[0];
        }
        final long total = system.getTotalMemory() / MEBI_BYTE;
        final long heap = DoubleMath.roundToLong(total * relativeHeapSize, RoundingMode.UP);
        final long free = system.getFreeMemory() / MEBI_BYTE;
        log.debug("using {}MiB as heap size (~{}% of total {}Mib)", heap, relativeHeapSize * 100, total);
        if (heap > free) {
            log.error("calculated heap size of {}MiB is greater than current free amount of memory ({}MiB)", heap,
                    free);
        }
        final String metaSpaceArg = system.getJvmSpecVersion() > 7
                ? "-XX:MaxMetaspaceSize=" + DoubleMath.roundToLong(total * 0.5, RoundingMode.CEILING)
                : "-XX:MaxPermSize=" + Math.max(128, DoubleMath.roundToLong(total * 0.05, RoundingMode.CEILING));
        return new String[] { "-Xms" + heap + "M", "-Xmx" + heap + "M", metaSpaceArg };
    }

    // toggles

    /**
     * Force the JVM to use the IPv4 stack, instead of favoring IPv6
     */
    public JvmOptions forceIPv4() {
        forceIPv4 = true;
        return this;
    }

    public JvmOptions home(final Path folder) {
        home = asDirectoryPath(folder);
        return this;
    }

    public JvmOptions relativeHeapSize(final double percentage) {
        Preconditions.checkArgument(percentage > 0.0 && percentage < 1.0,
                "illegal relative heap size " + percentage);
        this.relativeHeapSize = percentage;
        return this;
    }
}