/* Copyright 2012 Google, Inc. * * 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. */ package org.arbeitspferde.groningen.experimentdb; import com.google.common.base.Joiner; import com.google.common.base.Preconditions; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableList.Builder; import org.arbeitspferde.groningen.experimentdb.jvmflags.GcMode; import org.arbeitspferde.groningen.experimentdb.jvmflags.JvmFlag; import org.arbeitspferde.groningen.experimentdb.jvmflags.JvmFlagSet; import java.util.logging.Logger; /* * TODO(team): Migrate these methods that use long method signatures into ones that take some * defined, immutable data structure. */ /** * CommandLine represents subject command line data as it passes through the * {@link ExperimentDb}. Stored on {@link SubjectStateBridge}s. */ public class CommandLine { private static final Logger logger = Logger.getLogger(CommandLine.class.getCanonicalName()); private static final long FALSE_AS_LONG = 0L; private static final long TRUE_AS_LONG = 1L; private static final Joiner spaceSeparator = Joiner.on(" "); private final JvmFlagSet jvmFlagSet; // TODO(team): Move the regexp. generation to JvmFlag. // We assume the user controls and sets the -XX:MaxPermSize and -XX:PermSize outside Groningen. // There is no need to search the space, since it the permgen is a straight forward computation. // // complete list of flags with types and default values can be found via: // * java --XX:+PrintFlagsFinal // * potentially not all: // //depot/vendor_src/java/jdk/hotspot/src/share/vm/runtime/globals.hpp // boolean flags have the form -XX:[+-]<flag> private static final ImmutableList<String> MANAGED_ARGS_REGEXES = ImmutableList.<String>builder() .add(JvmFlag.ADAPTIVE_SIZE_DECREMENT_SCALE_FACTOR.asRegularExpressionString()) .add(JvmFlag.CMS_EXP_AVG_FACTOR.asRegularExpressionString()) .add(JvmFlag.CMS_INCREMENTAL_DUTY_CYCLE.asRegularExpressionString()) .add(JvmFlag.CMS_INCREMENTAL_DUTY_CYCLE_MIN.asRegularExpressionString()) .add(JvmFlag.CMS_INCREMENTAL_OFFSET.asRegularExpressionString()) .add(JvmFlag.CMS_INCREMENTAL_SAFETY_FACTOR.asRegularExpressionString()) .add(JvmFlag.CMS_INITIATING_OCCUPANCY_FRACTION.asRegularExpressionString()) .add(JvmFlag.GC_TIME_RATIO.asRegularExpressionString()) .add(JvmFlag.MAX_GC_PAUSE_MILLIS.asRegularExpressionString()) .add(JvmFlag.MAX_HEAP_FREE_RATIO.asRegularExpressionString()) .add(JvmFlag.MAX_NEW_SIZE.asRegularExpressionString()) .add(JvmFlag.MIN_HEAP_FREE_RATIO.asRegularExpressionString()) .add(JvmFlag.NEW_RATIO.asRegularExpressionString()) .add(JvmFlag.NEW_SIZE.asRegularExpressionString()) .add(JvmFlag.PARALLEL_GC_THREADS.asRegularExpressionString()) .add(JvmFlag.SOFT_REF_LRU_POLICY_MS_PER_MB.asRegularExpressionString()) .add(JvmFlag.SURVIVOR_RATIO.asRegularExpressionString()) .add(JvmFlag.TENURED_GENERATION_SIZE_INCREMENT.asRegularExpressionString()) .add(JvmFlag.YOUNG_GENERATION_SIZE_INCREMENT.asRegularExpressionString()) .add(JvmFlag.CMS_INCREMENTAL_MODE.asRegularExpressionString()) .add(JvmFlag.CMS_INCREMENTAL_PACING.asRegularExpressionString()) .add(JvmFlag.USE_CMS_INITIATING_OCCUPANCY_ONLY.asRegularExpressionString()) .add(JvmFlag.USE_CONC_MARK_SWEEP_GC.asRegularExpressionString()) .add(JvmFlag.USE_PARALLEL_GC.asRegularExpressionString()) .add(JvmFlag.USE_PARALLEL_OLD_GC.asRegularExpressionString()) .add(JvmFlag.USE_SERIAL_GC.asRegularExpressionString()) .add("-Xms\\d+[bBkKmMgG]?") .add("-Xmx\\d+[bBkKmMgG]?") .build(); /** * Retrieves an {@link ImmutableList} of the arguments that Groningen will manage. * * TODO(team): should these be regexes? Look deeper into the forms of the * flags and overlap in stems */ public static ImmutableList<String> getManagedArgs() { return MANAGED_ARGS_REGEXES; } /** Creation by package only */ CommandLine(final JvmFlagSet jvmFlagSet) { this.jvmFlagSet = Preconditions.checkNotNull(jvmFlagSet); } /** * Returns the value for the given command-line argument. * * @param argument Command-line argument. * @return Value for the given {@code argument}. * @throws IllegalArgumentException If the {@code argument} is null. */ public long getValue(final JvmFlag argument) { Preconditions.checkNotNull(argument, "Command-line argument cannot be null."); return jvmFlagSet.getValue(argument); } public long getHeapSize() { return getValue(JvmFlag.HEAP_SIZE); } public long getAdaptiveSizeDecrementScaleFactor() { return getValue(JvmFlag.ADAPTIVE_SIZE_DECREMENT_SCALE_FACTOR); } public long getCmsExpAvgFactor() { return getValue(JvmFlag.CMS_EXP_AVG_FACTOR); } public long getCmsIncrementalDutyCycle() { return getValue(JvmFlag.CMS_INCREMENTAL_DUTY_CYCLE); } public long getCmsIncrementalDutyCycleMin() { return getValue(JvmFlag.CMS_INCREMENTAL_DUTY_CYCLE_MIN); } public long getCmsIncrementalOffset() { return getValue(JvmFlag.CMS_INCREMENTAL_OFFSET); } public long getCmsIncrementalSafetyFactor() { return getValue(JvmFlag.CMS_INCREMENTAL_SAFETY_FACTOR); } public long getCmsInitiatingOccupancyFraction() { return getValue(JvmFlag.CMS_INITIATING_OCCUPANCY_FRACTION); } public long getGcTimeRatio() { return getValue(JvmFlag.GC_TIME_RATIO); } public long getMaxGcPauseMillis() { return getValue(JvmFlag.MAX_GC_PAUSE_MILLIS); } public long getMaxHeapFreeRatio() { return getValue(JvmFlag.MAX_HEAP_FREE_RATIO); } public long getMinHeapFreeRatio() { return getValue(JvmFlag.MIN_HEAP_FREE_RATIO); } public long getNewRatio() { return getValue(JvmFlag.NEW_RATIO); } public long getNewSize() { return getValue(JvmFlag.NEW_SIZE); } public long getParallelGCThreads() { return getValue(JvmFlag.PARALLEL_GC_THREADS); } public long getSurvivorRatio() { return getValue(JvmFlag.SURVIVOR_RATIO); } public long getTenuredGenerationSizeIncrement() { return getValue(JvmFlag.TENURED_GENERATION_SIZE_INCREMENT); } public long getYoungGenerationSizeIncrement() { return getValue(JvmFlag.YOUNG_GENERATION_SIZE_INCREMENT); } public long getSoftRefLruPolicyMsPerMb() { return getValue(JvmFlag.SOFT_REF_LRU_POLICY_MS_PER_MB); } public boolean getCmsIncrementalMode() { return getValue(JvmFlag.CMS_INCREMENTAL_MODE) == 1; } public boolean getCmsIncrementalPacing() { return getValue(JvmFlag.CMS_INCREMENTAL_PACING) == 1; } public boolean getUseCmsInitiatingOccupancyOnly() { return getValue(JvmFlag.USE_CMS_INITIATING_OCCUPANCY_ONLY) == 1; } public boolean getUseConcMarkSweepGC() { return getValue(JvmFlag.USE_CONC_MARK_SWEEP_GC) == 1; } public boolean getUseParallelGC() { return getValue(JvmFlag.USE_PARALLEL_GC) == 1; } public boolean getUseParallelOldGC() { return getValue(JvmFlag.USE_PARALLEL_OLD_GC) == 1; } public boolean getUseSerialGC() { return getValue(JvmFlag.USE_SERIAL_GC) == 1; } /** * Returns the garbage collection mode chosen in this command line. * * @return GC mode. * @throws RuntimeException If no GC mode is chosen. */ public GcMode getGcMode() { if (getUseConcMarkSweepGC()) { return GcMode.CMS; } else if (getUseParallelGC()) { return GcMode.PARALLEL; } else if (getUseParallelOldGC()) { return GcMode.PARALLEL_OLD; } else if (getUseSerialGC()) { return GcMode.SERIAL; } else { // TODO(team): more debug info. throw new IllegalStateException("No GC mode found."); } } /** * Convert this command line object into a JVM settings string. * * We only generate command line settings for non-zero integer arguments and * true valued boolean argmuments that are relevant to the selected GC Mode. * All other settings are effectively ignored and should not be present in the * generated JVM settings string. * * This method is intentionally not called {@link #toString()} due to EasyMock's inability * to mock it. * * @return a string representation of this command line object * @throws IllegalArgumentException */ public String toArgumentString() throws IllegalArgumentException { final Builder<String> argumentsBuilder = ImmutableList.builder(); // HEAP SIZING PARAMETERS // Since collections occur when generations fill up, throughput is inversely proportional to the // amount of memory available. Total available memory is the most important factor affecting // garbage collection performance. // // By default, the virtual machine grows or shrinks the heap at each collection to try to keep // the proportion of free space to live objects at each collection within a specific range. This // target range is set as a percentage by the parameters // -XX:MinHeapFreeRatio=<minimum> and -XX:MaxHeapFreeRatio=<maximum>, and the total size is // bounded below by -Xms and above by -Xmx // // We assume that setting either of these to 0 means ignore the setting if (getMinHeapFreeRatio() <= getMaxHeapFreeRatio()) { if (getMinHeapFreeRatio() > 0) { JvmFlag.MIN_HEAP_FREE_RATIO.validate(getMinHeapFreeRatio()); argumentsBuilder.add(JvmFlag.MIN_HEAP_FREE_RATIO .asArgumentString(getMinHeapFreeRatio())); } if (getMaxHeapFreeRatio() > 0) { JvmFlag.MAX_HEAP_FREE_RATIO.validate(getMaxHeapFreeRatio()); argumentsBuilder.add(JvmFlag.MAX_HEAP_FREE_RATIO .asArgumentString(getMaxHeapFreeRatio())); } } // At initialization of the virtual machine, the entire space for the heap is reserved. The size // of the space reserved can be specified with the -Xmx option. // // If the value of the -Xms parameter is smaller than the value of the -Xmx parameter, not all // of the space that is reserved is immediately committed to the JVM. // // Groningen sets Xmx equal to Xms because this is a common setting within Google. The reason // is this keeps the JVM from repeatedly allocating and de-allocating memory, thus reducing the // memory allocation overhead from program startup until the JVM wants to reclaim unused memory. // // The different parts of the heap (permanent generation, tenured // generation, and young // generation) can grow to the limit of the virtual space as needed. if (getHeapSize() > 0) { JvmFlag.HEAP_SIZE.validate(getHeapSize()); argumentsBuilder.add(String.format("-Xmx%sm", getHeapSize())); argumentsBuilder.add(String.format("-Xms%sm", getHeapSize())); } // We assume the user controls and sets the -XX:MaxPermSize and -XX:PermSize outside Groningen. // There is no need to search the space, since it the permgen is a straight forward // computation. // // TODO(team): We may want to permute PermSize in a future Groningen generation. // The bigger the young generation, the less often minor collections occur. However, for a // bounded heap size a larger young generation implies a smaller tenured generation, which will // increase the frequency of major collections. The optimal choice depends on the lifetime // distribution of the objects allocated by the application. // // By default, the young generation size is controlled by NewRatio. For example, setting // -XX:NewRatio=3 means that the ratio between the young and tenured generation is 1:3. In other // words, the combined size of the eden and survivor spaces will be one fourth of the total heap // size. // // We assume that setting newRatio to 0 means ignore the setting if (getNewRatio() > 0) { JvmFlag.NEW_RATIO.validate(getNewRatio()); argumentsBuilder.add(JvmFlag.NEW_RATIO.asArgumentString(getNewRatio())); } else // An eden bigger than half the virtually committed size of the heap is counterproductive. The // serial collector degrades in this scenario and major collections would occur. The throughput // collector and the concurrent collector will proceed with a young generation collection in // this scenario, but can often degrade into a full GC when the tenured generation cannot // accommodate all the promotions from the young generation. // // For this reason, we restrict the NewSize to less than 1/2 the heap size. // // TODO(team): Evaluate whether we're OK with invalid genes being generated. if (getNewSize() < (getHeapSize() / 2)) { // The parameters NewSize and MaxNewSize bound the young generation size from below and above. // Setting these equal to one another fixes the young generation, just as setting -Xms and // -Xmx equal fixes the total heap size. We assume that setting newSize to 0 means ignore the // setting // // The size is assumed to be in MB if (getNewSize() > 0) { JvmFlag.NEW_SIZE.validate(getNewSize()); argumentsBuilder.add(JvmFlag.NEW_SIZE.asArgumentString(getNewSize())); // Notice we are setting MaxNewSize to NewSize as this is a common thing // to do JvmFlag.MAX_NEW_SIZE.validate(getNewSize()); argumentsBuilder.add(JvmFlag.MAX_NEW_SIZE.asArgumentString(getNewSize())); } } // SurvivorRatio can be used to tune the size of the survivor spaces. We // assume 0 means ignore // this setting. if (getSurvivorRatio() > 0) { JvmFlag.SURVIVOR_RATIO.validate(getSurvivorRatio()); argumentsBuilder.add(JvmFlag.SURVIVOR_RATIO.asArgumentString(getSurvivorRatio())); } // Soft references are cleared less aggressively in the server virtual machine than the client. // The rate of clearing can be slowed by increasing the parameter SoftRefLRUPolicyMSPerMB with // the command line flag -XX:SoftRefLRUPolicyMSPerMB=10000. SoftRefLRUPolicyMSPerMB is a // measure of the time that a soft reference survives for a given amount of free space in the // heap. The default value is 1000 ms per megabyte. This can be read to mean that a soft // reference will survive (after the last strong reference to the object has been collected) // for 1 second for each megabyte of free space in the heap. This is very approximate. if (getSoftRefLruPolicyMsPerMb() > 0) { JvmFlag.SOFT_REF_LRU_POLICY_MS_PER_MB.validate(getSoftRefLruPolicyMsPerMb()); argumentsBuilder.add(JvmFlag.SOFT_REF_LRU_POLICY_MS_PER_MB .asArgumentString(getSoftRefLruPolicyMsPerMb())); } // COLLECTOR SPECFIC PARAMETERS if (getUseConcMarkSweepGC()) { // The Concurrent Low Pause Collector // // Use the concurrent low pause collector if your application would benefit from shorter // garbage collector pauses and can afford to share processor resources with the garbage // collector when the application is running. Typically applications which have a relatively // large set of long-lived data (a large tenured generation), and run on machines with two // or more processors tend to benefit from the use of this collector. argumentsBuilder.add(JvmFlag.USE_CONC_MARK_SWEEP_GC.asArgumentString(TRUE_AS_LONG)); // Normally, the concurrent collector uses one processor for the concurrent work for the // entire concurrent mark phase, without (voluntarily) relinquishing it. Similarly, one // processor is used for the entire concurrent sweep phase, again without relinquishing it. // This processor utilization can be too much of a disruption for applications with pause // time constraints, particularly when run on systems with just one or two processors. // i-cms solves this problem by breaking up the concurrent phases into short bursts of // activity, which are scheduled to occur mid-way between minor pauses. // // I-cms uses a "duty cycle" to control the amount of work the concurrent collector is allowed // to do before voluntarily giving up the processor. The duty cycle is the percentage of time // between young generation collections that the concurrent collector is allowed to run. I-cms // can automatically compute the duty cycle based on the behavior of the application (the // recommended method), or the duty cycle can be set to a fixed value on the command line. if (getCmsIncrementalMode()) { argumentsBuilder.add(JvmFlag.CMS_INCREMENTAL_MODE.asArgumentString(TRUE_AS_LONG)); // This flag enables automatic adjustment of the incremental mode duty cycle based on // statistics collected while the JVM is running. if (getCmsIncrementalPacing()) { argumentsBuilder.add(JvmFlag.CMS_INCREMENTAL_PACING.asArgumentString(TRUE_AS_LONG)); // This is the percentage (0-100) which is the lower bound on the duty // cycle when // CMSIncrementalPacing is enabled. // // We assume that setting this to 0 means ignore the setting if (getCmsIncrementalDutyCycleMin() > 0) { JvmFlag.CMS_INCREMENTAL_DUTY_CYCLE_MIN.validate(getCmsIncrementalDutyCycle()); argumentsBuilder.add(JvmFlag.CMS_INCREMENTAL_DUTY_CYCLE_MIN .asArgumentString(getCmsIncrementalDutyCycleMin())); } } // This is the percentage (0-100) used to weight the current sample when computing // exponential averages for the concurrent collection statistics. // // We assume that setting this to 0 means ignore the setting if (getCmsExpAvgFactor() > 0) { JvmFlag.CMS_EXP_AVG_FACTOR.validate(getCmsExpAvgFactor()); argumentsBuilder.add(JvmFlag.CMS_EXP_AVG_FACTOR .asArgumentString(getCmsExpAvgFactor())); } // This is the percentage (0-100) of time between minor collections that the concurrent // collector is allowed to run. If CMSIncrementalPacing is enabled, then this is just the // initial value. // // We assume that setting this to 0 means ignore the setting if (getCmsIncrementalDutyCycle() > 0) { JvmFlag.CMS_INCREMENTAL_DUTY_CYCLE.validate(getCmsIncrementalDutyCycle()); argumentsBuilder.add(JvmFlag.CMS_INCREMENTAL_DUTY_CYCLE .asArgumentString(getCmsIncrementalDutyCycle())); } // This is the percentage (0-100) by which the incremental mode duty cycle is shifted to the // right within the period between minor collections. // // We assume that setting this to 0 means ignore the setting if (getCmsIncrementalOffset() > 0) { JvmFlag.CMS_INCREMENTAL_OFFSET.validate(getCmsIncrementalOffset()); argumentsBuilder.add(JvmFlag.CMS_INCREMENTAL_OFFSET .asArgumentString(getCmsIncrementalOffset())); } // This is the percentage (0-100) used to add conservatism when computing the duty cycle. if (getCmsIncrementalSafetyFactor() > 0) { JvmFlag.CMS_INCREMENTAL_SAFETY_FACTOR.validate(getCmsIncrementalSafetyFactor()); argumentsBuilder.add(JvmFlag.CMS_INCREMENTAL_SAFETY_FACTOR .asArgumentString(getCmsIncrementalSafetyFactor())); } } // This parameter sets the threshold percentage occupancy of the old generation at which the // CMS GC is triggered. Setting this parameter explicitly tells the JVM when to trigger CMS // GC in the tenured generation. if (getCmsInitiatingOccupancyFraction() > 0) { JvmFlag.CMS_INITIATING_OCCUPANCY_FRACTION.validate(getCmsInitiatingOccupancyFraction()); argumentsBuilder.add(JvmFlag.CMS_INITIATING_OCCUPANCY_FRACTION .asArgumentString((getCmsInitiatingOccupancyFraction()))); // This parameter tells the JVM to use only the value defined by // -XX:CMSInitiatingOccupancyFraction, rather than try to also calculate // the value at // runtime. argumentsBuilder.add(JvmFlag.USE_CMS_INITIATING_OCCUPANCY_ONLY .asArgumentString(getUseCmsInitiatingOccupancyOnly() ? TRUE_AS_LONG : FALSE_AS_LONG)); } } else if (getUseParallelGC() || getUseParallelOldGC()) { // The Throughput Collector // // This is a generational collector similar to the serial collector but with multiple threads // used to do the minor collection. The major collections are essentially the same as with the // serial collector. By default on a host with N CPUs, the throughput collector uses N garbage // collector threads in the minor collection. The number of garbage collector threads can be // controlled with a command line option (see below). On a host with 1 CPU the throughput // collector will likely not perform as well as the serial collector because of the additional // overhead for the parallel execution (e.g., synchronization costs). On a host with 2 CPUs // the throughput collector generally performs as well as the serial garbage collector and a // reduction in the minor garbage collector pause times can be expected on hosts with more // than 2 CPUs. // Young Generation GC done in parallel threads argumentsBuilder.add(JvmFlag.USE_PARALLEL_GC.asArgumentString( getUseParallelGC() ? TRUE_AS_LONG : FALSE_AS_LONG)); // Certain phases of an ‘Old Generation’ collection can be performed in parallel, speeding // up a old generation collection. argumentsBuilder.add(JvmFlag.USE_PARALLEL_OLD_GC.asArgumentString(getUseParallelOldGC() ? TRUE_AS_LONG : FALSE_AS_LONG)); // Growing and shrinking the size of a generation is done by increments that are a fixed // percentage of the size of the generation. A generation steps up or down toward its // desired size. Growing and shrinking are done at different rates. By default a generation // grows in increments of 20% and shrinks in increments of 5%. The percentage for growing // is controlled by the command line flag -XX:YoungGenerationSizeIncrement=<nnn > for the // young generation and -XX:TenuredGenerationSizeIncrement=<nnn> for the tenured generation. // // We assume 0 means ignore the setting. if (getYoungGenerationSizeIncrement() > 0) { JvmFlag.YOUNG_GENERATION_SIZE_INCREMENT.validate(getYoungGenerationSizeIncrement()); argumentsBuilder.add(JvmFlag.YOUNG_GENERATION_SIZE_INCREMENT .asArgumentString(getYoungGenerationSizeIncrement())); } if (getTenuredGenerationSizeIncrement() > 0) { JvmFlag.TENURED_GENERATION_SIZE_INCREMENT.validate(getTenuredGenerationSizeIncrement()); argumentsBuilder.add(JvmFlag.TENURED_GENERATION_SIZE_INCREMENT .asArgumentString(getTenuredGenerationSizeIncrement())); } // The percentage by which a generation shrinks is adjusted by the command line flag // -XX: AdaptiveSizeDecrementScaleFactor=<nnn >. If the size of an increment for growing // is XXX percent, the size of the decrement for shrinking will be XXX / nnn percent. // // We assume that setting this to 0 means ignore the setting if (getAdaptiveSizeDecrementScaleFactor() > 0) { JvmFlag.ADAPTIVE_SIZE_DECREMENT_SCALE_FACTOR .validate(getAdaptiveSizeDecrementScaleFactor()); argumentsBuilder.add(JvmFlag.ADAPTIVE_SIZE_DECREMENT_SCALE_FACTOR .asArgumentString(getAdaptiveSizeDecrementScaleFactor())); } // A hint to the virtual machine that it's desirable that not more than 1 / (1 + nnn) of the // application execution time be spent in the collector. // // We assume that setting this to 0 means ignore the setting if (getGcTimeRatio() > 0) { JvmFlag.GC_TIME_RATIO.validate(getGcTimeRatio()); argumentsBuilder.add(JvmFlag.GC_TIME_RATIO.asArgumentString(getGcTimeRatio())); } // This is interpreted as a hint to the throughput collector that pause times of <nnn> // milliseconds or less are desired. By default there is no maximum pause time goal. The // throughput collector will adjust the Java heap size and other garbage collection related // parameters in an attempt to keep garbage collection pauses shorter than <nnn> milliseconds. // These adjustments may cause the garbage collector to reduce overall throughput of the // application and in some cases the desired pause time goal cannot be met. // // We assume that setting this to 0 means ignore the setting if (getMaxHeapFreeRatio() > 0) { JvmFlag.MAX_GC_PAUSE_MILLIS.validate(getMaxGcPauseMillis()); argumentsBuilder.add(JvmFlag.MAX_GC_PAUSE_MILLIS .asArgumentString(getMaxGcPauseMillis())); } // The number of garbage collector threads can be controlled with the ParallelGCThreads // command line option. We assume 0 implies do not set this parameter. if (getParallelGCThreads() > 0) { JvmFlag.PARALLEL_GC_THREADS.validate(getParallelGCThreads()); argumentsBuilder.add(JvmFlag.PARALLEL_GC_THREADS .asArgumentString(getParallelGCThreads())); } } else if (getUseSerialGC()) { // The Serial Collector // // This is a simple serial generational garbage collector that performs well on a single // CPU. It uses resources efficiently, pauses application threads and its performance is // inversely proportional to the size of the heap. // // It is the best choice for a single CPU and a small heap. argumentsBuilder.add(JvmFlag.USE_SERIAL_GC.asArgumentString(TRUE_AS_LONG)); } final String emission = spaceSeparator.join(argumentsBuilder.build()); logger.finer(String.format("Converted %s into %s.", jvmFlagSet, emission)); return emission; } @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; CommandLine that = (CommandLine) o; if (!jvmFlagSet.equals(that.jvmFlagSet)) return false; return true; } @Override public int hashCode() { return jvmFlagSet.hashCode(); } }