changeset 354:7010d38ef764

Rework forked JVM options: purge "classpath", make use of Optional, fix a few minor bugs, print JVM options in the human-readable log.
author shade
date Mon, 20 Jan 2014 20:04:34 +0400
parents e451fc86654b
children 8d1e26a55e09
files jmh-core-it/src/test/java/org/openjdk/jmh/it/fork/ForkedJvmArgs3_Test.java jmh-core/src/main/java/org/openjdk/jmh/annotations/Fork.java jmh-core/src/main/java/org/openjdk/jmh/processor/internal/MethodGroup.java jmh-core/src/main/java/org/openjdk/jmh/runner/BenchmarkRecord.java jmh-core/src/main/java/org/openjdk/jmh/runner/Runner.java jmh-core/src/main/java/org/openjdk/jmh/runner/options/ChainedOptionsBuilder.java jmh-core/src/main/java/org/openjdk/jmh/runner/options/CommandLineOptions.java jmh-core/src/main/java/org/openjdk/jmh/runner/options/Options.java jmh-core/src/main/java/org/openjdk/jmh/runner/options/OptionsBuilder.java jmh-core/src/main/java/org/openjdk/jmh/util/AnnotationUtils.java jmh-core/src/main/java/org/openjdk/jmh/util/internal/Optional.java jmh-core/src/test/java/org/openjdk/jmh/runner/options/TestOptions.java
diffstat 12 files changed, 150 insertions(+), 224 deletions(-) [+]
line wrap: on
line diff
--- a/jmh-core-it/src/test/java/org/openjdk/jmh/it/fork/ForkedJvmArgs3_Test.java	Mon Jan 20 14:44:17 2014 +0400
+++ b/jmh-core-it/src/test/java/org/openjdk/jmh/it/fork/ForkedJvmArgs3_Test.java	Mon Jan 20 20:04:34 2014 +0400
@@ -50,7 +50,7 @@
     @GenerateMicroBenchmark
     @Warmup(iterations = 0)
     @Measurement(iterations = 1, time = 100, timeUnit = TimeUnit.MILLISECONDS)
-    @Fork(jvmArgs = "-Dtest1  -Dtest3")
+    @Fork(jvmArgs = {"-Dtest1", "-Dtest3"})
     public void test1() {
         Fixtures.work();
         Assert.assertNotNull(System.getProperty("test1"));
@@ -61,7 +61,7 @@
     @GenerateMicroBenchmark
     @Warmup(iterations = 0)
     @Measurement(iterations = 1, time = 100, timeUnit = TimeUnit.MILLISECONDS)
-    @Fork(jvmArgs = "-Dtest2  -Dtest3")
+    @Fork(jvmArgs = {"-Dtest2", "-Dtest3"})
     public void test2() {
         Fixtures.work();
         Assert.assertNull(System.getProperty("test1"));
--- a/jmh-core/src/main/java/org/openjdk/jmh/annotations/Fork.java	Mon Jan 20 14:44:17 2014 +0400
+++ b/jmh-core/src/main/java/org/openjdk/jmh/annotations/Fork.java	Mon Jan 20 20:04:34 2014 +0400
@@ -24,8 +24,6 @@
  */
 package org.openjdk.jmh.annotations;
 
-import org.openjdk.jmh.util.AnnotationUtils;
-
 import java.lang.annotation.ElementType;
 import java.lang.annotation.Inherited;
 import java.lang.annotation.Retention;
@@ -46,20 +44,21 @@
 
     public static final int BLANK_FORKS = -1;
 
+    public static final String BLANK_ARGS = "blank_blank_blank_2014";
+
     /** specifies number of times harness should fork, zero means "no fork" */
     int value() default BLANK_FORKS;
 
     /** enforce strict JVM args, replaces any implicit jvm args */
-    String jvmArgs() default AnnotationUtils.PARAM_NOT_SET;
+    String[] jvmArgs() default { BLANK_ARGS };
 
     /** prepend these arguments in the command line */
-    String jvmArgsPrepend() default AnnotationUtils.PARAM_NOT_SET;
+    String[] jvmArgsPrepend() default { BLANK_ARGS };
 
     /** append these arguments in the command line */
-    String jvmArgsAppend() default AnnotationUtils.PARAM_NOT_SET;
+    String[] jvmArgsAppend() default { BLANK_ARGS };
 
     /** ignore results first warmups forks */
     int warmups() default BLANK_FORKS;
 
-}
-
+}
\ No newline at end of file
--- a/jmh-core/src/main/java/org/openjdk/jmh/processor/internal/MethodGroup.java	Mon Jan 20 14:44:17 2014 +0400
+++ b/jmh-core/src/main/java/org/openjdk/jmh/processor/internal/MethodGroup.java	Mon Jan 20 20:04:34 2014 +0400
@@ -32,12 +32,12 @@
 import org.openjdk.jmh.annotations.Threads;
 import org.openjdk.jmh.annotations.Warmup;
 import org.openjdk.jmh.runner.parameters.TimeValue;
-import org.openjdk.jmh.util.AnnotationUtils;
 import org.openjdk.jmh.util.internal.Optional;
 
 import javax.lang.model.element.Element;
 import java.lang.annotation.Annotation;
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.Collection;
 import java.util.Collections;
 import java.util.EnumSet;
@@ -190,26 +190,26 @@
         return Optional.none();
     }
 
-    public Optional<String> getJVMArgs() {
+    public Optional<Collection<String>> getJVMArgs() {
         Fork ann = getFinal(Fork.class);
-        if (ann != null && !ann.jvmArgs().equals(AnnotationUtils.PARAM_NOT_SET)) {
-            return Optional.of(ann.jvmArgs());
+        if (ann != null && !(ann.jvmArgs().length == 1 && ann.jvmArgs()[0].equals(Fork.BLANK_ARGS))) {
+            return Optional.<Collection<String>>of(Arrays.asList(ann.jvmArgs()));
         }
         return Optional.none();
     }
 
-    public Optional<String> getJVMArgsAppend() {
+    public Optional<Collection<String>> getJVMArgsAppend() {
         Fork ann = getFinal(Fork.class);
-        if (ann != null && !ann.jvmArgsAppend().equals(AnnotationUtils.PARAM_NOT_SET)) {
-            return Optional.of(ann.jvmArgsAppend());
+        if (ann != null && !(ann.jvmArgsAppend().length == 1 && ann.jvmArgsAppend()[0].equals(Fork.BLANK_ARGS))) {
+            return Optional.<Collection<String>>of(Arrays.asList(ann.jvmArgsAppend()));
         }
         return Optional.none();
     }
 
-    public Optional<String> getJVMArgsPrepend() {
+    public Optional<Collection<String>> getJVMArgsPrepend() {
         Fork ann = getFinal(Fork.class);
-        if (ann != null && !ann.jvmArgsPrepend().equals(AnnotationUtils.PARAM_NOT_SET)) {
-            return Optional.of(ann.jvmArgsPrepend());
+        if (ann != null && !(ann.jvmArgsPrepend().length == 1 && ann.jvmArgsPrepend()[0].equals(Fork.BLANK_ARGS))) {
+            return Optional.<Collection<String>>of(Arrays.asList(ann.jvmArgsPrepend()));
         }
         return Optional.none();
     }
--- a/jmh-core/src/main/java/org/openjdk/jmh/runner/BenchmarkRecord.java	Mon Jan 20 14:44:17 2014 +0400
+++ b/jmh-core/src/main/java/org/openjdk/jmh/runner/BenchmarkRecord.java	Mon Jan 20 20:04:34 2014 +0400
@@ -29,6 +29,7 @@
 import org.openjdk.jmh.util.internal.Optional;
 
 import java.io.Serializable;
+import java.util.Collection;
 
 public class BenchmarkRecord implements Comparable<BenchmarkRecord>, Serializable {
 
@@ -43,14 +44,14 @@
     private final Optional<TimeValue> measurementTime;
     private final Optional<Integer> forks;
     private final Optional<Integer> warmupForks;
-    private final Optional<String> jvmArgs;
-    private final Optional<String> jvmArgsPrepend;
-    private final Optional<String> jvmArgsAppend;
+    private final Optional<Collection<String>> jvmArgs;
+    private final Optional<Collection<String>> jvmArgsPrepend;
+    private final Optional<Collection<String>> jvmArgsAppend;
 
     public BenchmarkRecord(String userName, String generatedName, Mode mode, int[] threadGroups, Optional<Integer> threads,
                            Optional<Integer> warmupIterations, Optional<TimeValue> warmupTime,
                            Optional<Integer> measurementIterations, Optional<TimeValue> measurementTime,
-                           Optional<Integer> forks, Optional<Integer> warmupForks, Optional<String> jvmArgs, Optional<String> jvmArgsPrepend, Optional<String> jvmArgsAppend) {
+                           Optional<Integer> forks, Optional<Integer> warmupForks, Optional<Collection<String>> jvmArgs, Optional<Collection<String>> jvmArgsPrepend, Optional<Collection<String>> jvmArgsAppend) {
         this.userName = userName;
         this.generatedName = generatedName;
         this.mode = mode;
@@ -78,28 +79,31 @@
         this.generatedName = args[1].trim();
         this.mode = Mode.deepValueOf(args[2].trim());
         this.threadGroups = convert(args[3].split("="));
-        this.threads = Optional.of(args[4], Optional.INTEGER_EXTRACTOR);
-        this.warmupIterations = Optional.of(args[5], Optional.INTEGER_EXTRACTOR);
-        this.warmupTime = Optional.of(args[6], Optional.TIME_VALUE_EXTRACTOR);
-        this.measurementIterations = Optional.of(args[7], Optional.INTEGER_EXTRACTOR);
-        this.measurementTime = Optional.of(args[8], Optional.TIME_VALUE_EXTRACTOR);
-        this.forks = Optional.of(args[9], Optional.INTEGER_EXTRACTOR);
-        this.warmupForks = Optional.of(args[10], Optional.INTEGER_EXTRACTOR);
-        this.jvmArgs = Optional.of(args[11], Optional.STRING_EXTRACTOR);
-        this.jvmArgsPrepend = Optional.of(args[12], Optional.STRING_EXTRACTOR);
-        this.jvmArgsAppend = Optional.of(args[13], Optional.STRING_EXTRACTOR);
+        this.threads = Optional.of(args[4], Optional.INTEGER_UNMARSHALLER);
+        this.warmupIterations = Optional.of(args[5], Optional.INTEGER_UNMARSHALLER);
+        this.warmupTime = Optional.of(args[6], Optional.TIME_VALUE_UNMARSHALLER);
+        this.measurementIterations = Optional.of(args[7], Optional.INTEGER_UNMARSHALLER);
+        this.measurementTime = Optional.of(args[8], Optional.TIME_VALUE_UNMARSHALLER);
+        this.forks = Optional.of(args[9], Optional.INTEGER_UNMARSHALLER);
+        this.warmupForks = Optional.of(args[10], Optional.INTEGER_UNMARSHALLER);
+        this.jvmArgs = Optional.of(args[11], Optional.STRING_COLLECTION_UNMARSHALLER);
+        this.jvmArgsPrepend = Optional.of(args[12], Optional.STRING_COLLECTION_UNMARSHALLER);
+        this.jvmArgsAppend = Optional.of(args[13], Optional.STRING_COLLECTION_UNMARSHALLER);
     }
 
     public BenchmarkRecord(String userName, String generatedName, Mode mode) {
         this(userName, generatedName, mode, new int[]{}, Optional.<Integer>none(),
                 Optional.<Integer>none(), Optional.<TimeValue>none(), Optional.<Integer>none(), Optional.<TimeValue>none(),
-                Optional.<Integer>none(), Optional.<Integer>none(), Optional.<String>none(), Optional.<String>none(), Optional.<String>none());
+                Optional.<Integer>none(), Optional.<Integer>none(), Optional.<Collection<String>>none(), Optional.<Collection<String>>none(), Optional.<Collection<String>>none());
     }
 
     public String toLine() {
         return userName + "," + generatedName + "," + mode + "," + convert(threadGroups) + "," + threads + "," +
                 warmupIterations + "," + warmupTime + "," + measurementIterations + "," + measurementTime + "," +
-                forks + "," + warmupForks + "," + jvmArgs + "," + jvmArgsPrepend + "," + jvmArgsAppend;
+                forks + "," + warmupForks + "," +
+                jvmArgs.toString(Optional.STRING_COLLECTION_MARSHALLER) + "," +
+                jvmArgsPrepend.toString(Optional.STRING_COLLECTION_MARSHALLER) + "," +
+                jvmArgsAppend.toString(Optional.STRING_COLLECTION_MARSHALLER);
     }
 
     public BenchmarkRecord cloneWith(Mode mode) {
@@ -220,15 +224,15 @@
         return warmupForks;
     }
 
-    public Optional<String> getJvmArgs() {
+    public Optional<Collection<String>> getJvmArgs() {
         return jvmArgs;
     }
 
-    public Optional<String> getJvmArgsAppend() {
+    public Optional<Collection<String>> getJvmArgsAppend() {
         return jvmArgsAppend;
     }
 
-    public Optional<String> getJvmArgsPrepend() {
+    public Optional<Collection<String>> getJvmArgsPrepend() {
         return jvmArgsPrepend;
     }
 
--- a/jmh-core/src/main/java/org/openjdk/jmh/runner/Runner.java	Mon Jan 20 14:44:17 2014 +0400
+++ b/jmh-core/src/main/java/org/openjdk/jmh/runner/Runner.java	Mon Jan 20 20:04:34 2014 +0400
@@ -321,30 +321,28 @@
 
             BenchmarkRecord benchmark = actionPlan.getMeasurementActions().get(0).getBenchmark();
 
-                String annJvmArgs = benchmark.getJvmArgs().orElse(null);
-                String annJvmArgsAppend = benchmark.getJvmArgsAppend().orElse(null);
-                String annJvmArgsPrepend = benchmark.getJvmArgsPrepend().orElse(null);
+            String[] commandString = getSeparateExecutionCommand(benchmark, server.getHost(), server.getPort());
 
-                String[] commandString = getSeparateExecutionCommand(annJvmArgs, annJvmArgsPrepend, annJvmArgsAppend, server.getHost(), server.getPort());
+            BenchmarkParams params = new BenchmarkParams(options, benchmark, ActionMode.UNDEF);
 
-                BenchmarkParams params = new BenchmarkParams(options, benchmark, ActionMode.UNDEF);
+            int forkCount = params.getForks();
+            int warmupForkCount = params.getWarmupForks();
+            if (warmupForkCount > 0) {
+                out.verbosePrintln("Warmup forking " + warmupForkCount + " times using command: " + Arrays.toString(commandString));
+                for (int i = 0; i < warmupForkCount; i++) {
+                    out.println("# Warmup Fork: " + (i + 1) + " of " + forkCount);
+                    out.println("# VM options: " + merge(options.getJvmArgs().orElse(benchmark.getJvmArgs().orElse(getDefaultJvmArgs(benchmark)))));
+                    doFork(server, commandString);
+                }
+            }
 
-                int forkCount = params.getForks();
-                int warmupForkCount = params.getWarmupForks();
-                if (warmupForkCount > 0) {
-                    out.verbosePrintln("Warmup forking " + warmupForkCount + " times using command: " + Arrays.toString(commandString));
-                    for (int i = 0; i < warmupForkCount; i++) {
-                        out.println("# Warmup Fork: " + (i + 1) + " of " + forkCount);
-                        doFork(server, commandString);
-                    }
-                }
-
-                out.verbosePrintln("Forking " + forkCount + " times using command: " + Arrays.toString(commandString));
-                for (int i = 0; i < forkCount; i++) {
-                    out.println("# Fork: " + (i + 1) + " of " + forkCount);
-                    Multimap<BenchmarkRecord, BenchResult> result = doFork(server, commandString);
-                    results.merge(result);
-                }
+            out.verbosePrintln("Forking " + forkCount + " times using command: " + Arrays.toString(commandString));
+            for (int i = 0; i < forkCount; i++) {
+                out.println("# Fork: " + (i + 1) + " of " + forkCount);
+                out.println("# VM options: " + merge(options.getJvmArgs().orElse(benchmark.getJvmArgs().orElse(getDefaultJvmArgs(benchmark)))));
+                Multimap<BenchmarkRecord, BenchResult> result = doFork(server, commandString);
+                results.merge(result);
+            }
 
         } catch (IOException e) {
             throw new IllegalStateException(e);
@@ -357,6 +355,13 @@
         return results;
     }
 
+    private String merge(Collection<String> ss) {
+        StringBuilder sb = new StringBuilder();
+        for (String s : ss) {
+            sb.append(s).append(" ");
+        }
+        return sb.toString().trim();
+    }
 
     private Multimap<BenchmarkRecord, BenchResult> doFork(BinaryLinkServer reader, String[] commandString) {
         try {
@@ -397,72 +402,32 @@
     /**
      * Helper method for assembling the command to execute the forked JVM with
      *
+     *
+     * @param benchmark benchmark to execute
      * @param host host VM host
      * @param port host VM port
      * @return the final command to execute
      */
-    public String[] getSeparateExecutionCommand(String annJvmArgs, String annJvmArgsPrepend, String annJvmArgsAppend, String host, int port) {
-
-        Properties props = System.getProperties();
-        String javaHome = (String) props.get("java.home");
-        String separator = File.separator;
-        String osName = props.getProperty("os.name");
-        boolean isOnWindows = osName.contains("indows");
-        String platformSpecificBinaryPostfix = isOnWindows ? ".exe" : "";
-
-        String classPath = options.getJvmClassPath().orElse((String) props.get("java.class.path"));
-
-        if (isOnWindows) {
-            classPath = '"' + classPath + '"';
-        }
+    public String[] getSeparateExecutionCommand(BenchmarkRecord benchmark, String host, int port) {
 
         List<String> command = new ArrayList<String>();
 
-        // use supplied jvm if given
-        if (options.getJvm().hasValue()) {
-            command.add(options.getJvm().get());
-        } else {
-            // else find out which one parent is and use that
-            StringBuilder javaExecutable = new StringBuilder();
-            javaExecutable.append(javaHome);
-            javaExecutable.append(separator);
-            javaExecutable.append("bin");
-            javaExecutable.append(separator);
-            javaExecutable.append("java");
-            javaExecutable.append(platformSpecificBinaryPostfix);
-            command.add(javaExecutable.toString());
-        }
+        // use supplied jvm, if given
+        command.add(options.getJvm().orElse(getDefaultJvm()));
 
-        if (options.getJvmArgs().hasValue()) { // use supplied jvm args if given in cmd line
-            command.addAll(Arrays.asList(options.getJvmArgs().get().split("[ ]+")));
-        } else if (annJvmArgs != null) { // use jvm args supplied in annotation which shuns implicit args
-            command.addAll(Arrays.asList(annJvmArgs.split("[ ]+")));
-        } else {
-            // else use same jvm args given to this runner
-            RuntimeMXBean RuntimemxBean = ManagementFactory.getRuntimeMXBean();
-            List<String> args = RuntimemxBean.getInputArguments();
-
-            // prepend jvm args
-            if (annJvmArgsPrepend != null) {
-                command.addAll(Arrays.asList(annJvmArgsPrepend.split(" ")));
-            }
-
-            for (String arg : args) {
-                command.add(arg);
-            }
-
-            // append jvm args
-            if (annJvmArgsAppend != null) {
-                command.addAll(Arrays.asList(annJvmArgsAppend.split(" ")));
-            }
-        }
+        // use supplied jvm args, if given
+        command.addAll(options.getJvmArgs().orElse(benchmark.getJvmArgs().orElse(getDefaultJvmArgs(benchmark))));
 
         // add any compiler oracle hints
         command.add("-XX:CompileCommandFile=" + CompilerHints.hintsFile());
 
         // assemble final process command
         command.add("-cp");
-        command.add(classPath);
+        if (isWindows()) {
+            command.add('"' + System.getProperty("java.class.path") + '"');
+        } else {
+            command.add(System.getProperty("java.class.path"));
+        }
         command.add(ForkedMain.class.getName());
 
         // Forked VM assumes the exact order of arguments:
@@ -474,4 +439,27 @@
         return command.toArray(new String[command.size()]);
     }
 
+    private boolean isWindows() {
+        return System.getProperty("os.name").contains("indows");
+    }
+
+    private String getDefaultJvm() {
+        StringBuilder javaExecutable = new StringBuilder();
+        javaExecutable.append(System.getProperty("java.home"));
+        javaExecutable.append(File.separator);
+        javaExecutable.append("bin");
+        javaExecutable.append(File.separator);
+        javaExecutable.append("java");
+        javaExecutable.append(isWindows() ? ".exe" : "");
+        return javaExecutable.toString();
+    }
+
+    private Collection<String> getDefaultJvmArgs(BenchmarkRecord benchmark) {
+        Collection<String> res = new ArrayList<String>();
+        res.addAll(benchmark.getJvmArgsPrepend().orElse(Collections.<String>emptyList()));
+        res.addAll(ManagementFactory.getRuntimeMXBean().getInputArguments());
+        res.addAll(benchmark.getJvmArgsAppend().orElse(Collections.<String>emptyList()));
+        return res;
+    }
+
 }
--- a/jmh-core/src/main/java/org/openjdk/jmh/runner/options/ChainedOptionsBuilder.java	Mon Jan 20 14:44:17 2014 +0400
+++ b/jmh-core/src/main/java/org/openjdk/jmh/runner/options/ChainedOptionsBuilder.java	Mon Jan 20 20:04:34 2014 +0400
@@ -208,19 +208,11 @@
     ChainedOptionsBuilder jvm(String path);
 
     /**
-     * Forked JVM classpath.
-     *
-     * @param value classpath to override with
-     * @return builder
-     */
-    ChainedOptionsBuilder jvmClasspath(String value);
-
-    /**
      * Forked JVM arguments.
      *
      * @param value arguments to override with
      * @return builder
      */
-    ChainedOptionsBuilder jvmArgs(String value);
+    ChainedOptionsBuilder jvmArgs(String... value);
 
 }
--- a/jmh-core/src/main/java/org/openjdk/jmh/runner/options/CommandLineOptions.java	Mon Jan 20 14:44:17 2014 +0400
+++ b/jmh-core/src/main/java/org/openjdk/jmh/runner/options/CommandLineOptions.java	Mon Jan 20 20:04:34 2014 +0400
@@ -76,8 +76,7 @@
     private final Optional<String> result;
     private final Optional<ResultFormatType> resultFormat;
     private final Optional<String> jvm;
-    private final Optional<String> jvmArgs;
-    private final Optional<String> jvmClassPath;
+    private final Optional<Collection<String>> jvmArgs;
     private final List<String> excludes = new ArrayList<String>();
     private final Optional<WarmupMode> warmupMode;
     private final List<String> warmupMicros = new ArrayList<String>();
@@ -155,9 +154,6 @@
         OptionSpec<String> optJvmArgs = parser.accepts("jvmArgs", "Custom JVM args to use when forking.")
                 .withRequiredArg().ofType(String.class).describedAs("string");
 
-        OptionSpec<String> optJvmCP = parser.accepts("jvmClasspath", "Custom JVM classpath to use when forking.")
-                .withRequiredArg().ofType(String.class).describedAs("string");
-
         OptionSpec<String> optTU = parser.accepts("tu", "Output time unit. Available time units are: [m, s, ms, us, ns].")
                 .withRequiredArg().ofType(String.class).describedAs("TU");
 
@@ -369,9 +365,12 @@
             }
 
             jvm = Optional.eitherOf(optJvm.value(set));
-            jvmArgs = Optional.eitherOf(optJvmArgs.value(set));
-            jvmClassPath = Optional.eitherOf(optJvmCP.value(set));
 
+            if (set.hasArgument(optJvmArgs)) {
+                jvmArgs = Optional.<Collection<String>>of(Arrays.asList(optJvmArgs.value(set).trim().split("[ ]+")));
+            } else {
+                jvmArgs = Optional.none();
+            }
         } catch (OptionException e) {
             throw new CommandLineOptionException(e.getMessage(), e);
         }
@@ -486,7 +485,7 @@
      * @return the value
      */
     @Override
-    public Optional<String> getJvmArgs() {
+    public Optional<Collection<String>> getJvmArgs() {
         return jvmArgs;
     }
 
@@ -496,16 +495,6 @@
      * @return the value
      */
     @Override
-    public Optional<String> getJvmClassPath() {
-        return jvmClassPath;
-    }
-
-    /**
-     * Getter
-     *
-     * @return the value
-     */
-    @Override
     public Optional<Integer> getForkCount() {
         return fork;
     }
--- a/jmh-core/src/main/java/org/openjdk/jmh/runner/options/Options.java	Mon Jan 20 14:44:17 2014 +0400
+++ b/jmh-core/src/main/java/org/openjdk/jmh/runner/options/Options.java	Mon Jan 20 20:04:34 2014 +0400
@@ -170,12 +170,6 @@
     Optional<Integer> getWarmupForkCount();
 
     /**
-     * Additional JVM classpath
-     * @return additional JVM classpath to add to forked VM
-     */
-    Optional<String> getJvmClassPath();
-
-    /**
      * JVM to use for forks
      * @return JVM binary location
      */
@@ -185,6 +179,6 @@
      * JVM parameters to use with forks
      * @return JVM parameters
      */
-    Optional<String> getJvmArgs();
+    Optional<Collection<String>> getJvmArgs();
 
 }
--- a/jmh-core/src/main/java/org/openjdk/jmh/runner/options/OptionsBuilder.java	Mon Jan 20 14:44:17 2014 +0400
+++ b/jmh-core/src/main/java/org/openjdk/jmh/runner/options/OptionsBuilder.java	Mon Jan 20 20:04:34 2014 +0400
@@ -31,6 +31,7 @@
 import org.openjdk.jmh.util.internal.Optional;
 
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.Collection;
 import java.util.Collections;
 import java.util.EnumSet;
@@ -390,21 +391,6 @@
 
     // ---------------------------------------------------------------------------
 
-    private Optional<String> jvmClassPath = Optional.none();
-
-    @Override
-    public ChainedOptionsBuilder jvmClasspath(String value) {
-        this.jvmClassPath = Optional.of(value);
-        return this;
-    }
-
-    @Override
-    public Optional<String> getJvmClassPath() {
-        return jvmClassPath;
-    }
-
-    // ---------------------------------------------------------------------------
-
     private Optional<String> jvmBinary = Optional.none();
 
     @Override
@@ -420,16 +406,16 @@
 
     // ---------------------------------------------------------------------------
 
-    private Optional<String> jvmArgs = Optional.none();
+    private Optional<Collection<String>> jvmArgs = Optional.none();
 
     @Override
-    public ChainedOptionsBuilder jvmArgs(String value) {
-        this.jvmArgs = Optional.of(value);
+    public ChainedOptionsBuilder jvmArgs(String... value) {
+        this.jvmArgs = Optional.<Collection<String>>of(Arrays.asList(value));
         return this;
     }
 
     @Override
-    public Optional<String> getJvmArgs() {
+    public Optional<Collection<String>> getJvmArgs() {
         return jvmArgs;
     }
 
--- a/jmh-core/src/main/java/org/openjdk/jmh/util/AnnotationUtils.java	Mon Jan 20 14:44:17 2014 +0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,39 +0,0 @@
-/*
- * Copyright (c) 2005, 2013, Oracle and/or its affiliates. All rights reserved.
- * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
- *
- * This code is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License version 2 only, as
- * published by the Free Software Foundation.  Oracle designates this
- * particular file as subject to the "Classpath" exception as provided
- * by Oracle in the LICENSE file that accompanied this code.
- *
- * This code is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
- * version 2 for more details (a copy is included in the LICENSE file that
- * accompanied this code).
- *
- * You should have received a copy of the GNU General Public License version
- * 2 along with this work; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
- *
- * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
- * or visit www.oracle.com if you need additional information or have any
- * questions.
- */
-package org.openjdk.jmh.util;
-
-/**
- * Annotation auxiliary logic utility class.
- *
- * @author Sergey Kuksenko (sergey.kuksenko@oracle.com)
- */
-public class AnnotationUtils {
-
-    public static final String PARAM_NOT_SET = "NOT_SET_20122012"; // random String marking the not set value
-
-    public static boolean isSet(String value) {
-        return !PARAM_NOT_SET.equals(value);
-    }
-}
--- a/jmh-core/src/main/java/org/openjdk/jmh/util/internal/Optional.java	Mon Jan 20 14:44:17 2014 +0400
+++ b/jmh-core/src/main/java/org/openjdk/jmh/util/internal/Optional.java	Mon Jan 20 20:04:34 2014 +0400
@@ -27,6 +27,8 @@
 import org.openjdk.jmh.runner.parameters.TimeValue;
 
 import java.io.Serializable;
+import java.util.Arrays;
+import java.util.Collection;
 
 /**
  * Option class
@@ -73,15 +75,15 @@
     /**
      * Parse the existing string value into the Option
      * @param source source string
-     * @param extractor extractor lambda parsing the (String -> T)
+     * @param unmarshaller unmarshaller lambda parsing the (String -> T)
      * @param <T> type
      * @return value wrapped in the Option
      */
-    public static <T> Optional<T> of(String source, Extractor<T> extractor) {
+    public static <T> Optional<T> of(String source, Unmarshaller<T> unmarshaller) {
         if (source.equals("[]")) {
             return Optional.none();
         } else {
-            return Optional.of(extractor.valueOf(source.substring(1, source.length() - 1)));
+            return Optional.of(unmarshaller.valueOf(source.substring(1, source.length() - 1)));
         }
     }
 
@@ -105,6 +107,14 @@
         }
     }
 
+    public String toString(Marshaller<T> m) {
+        if (val == null) {
+            return "[]";
+        } else {
+            return "[" + m.valueOf(val) + "]";
+        }
+    }
+
     public T get() {
         if (val == null) {
             throw new IllegalStateException("Optional is null");
@@ -129,28 +139,43 @@
         return val != null ? val.hashCode() : 0;
     }
 
-    public interface Extractor<T> {
+    public interface Unmarshaller<T> {
         T valueOf(String s);
     }
 
-    public static final Extractor<Integer> INTEGER_EXTRACTOR = new Optional.Extractor<Integer>() {
+    public interface Marshaller<T> {
+        String valueOf(T val);
+    }
+
+    public static final Unmarshaller<Integer> INTEGER_UNMARSHALLER = new Unmarshaller<Integer>() {
         @Override
         public Integer valueOf(String s) {
             return Integer.valueOf(s);
         }
     };
 
-    public static final Extractor<TimeValue> TIME_VALUE_EXTRACTOR = new Optional.Extractor<TimeValue>() {
+    public static final Unmarshaller<TimeValue> TIME_VALUE_UNMARSHALLER = new Unmarshaller<TimeValue>() {
         @Override
         public TimeValue valueOf(String s) {
             return TimeValue.fromString(s);
         }
     };
 
-    public static final Extractor<String> STRING_EXTRACTOR = new Optional.Extractor<String>() {
+    public static final Unmarshaller<Collection<String>> STRING_COLLECTION_UNMARSHALLER = new Unmarshaller<Collection<String>>() {
         @Override
-        public String valueOf(String s) {
-            return s;
+        public Collection<String> valueOf(String s) {
+            return Arrays.asList(s.split("===SEP==="));
+        }
+    };
+
+    public static final Marshaller<Collection<String>> STRING_COLLECTION_MARSHALLER = new Optional.Marshaller<Collection<String>>() {
+        @Override
+        public String valueOf(Collection<String> src) {
+            StringBuilder sb = new StringBuilder();
+            for (String s : src) {
+                sb.append(s).append("===SEP===");
+            }
+            return sb.toString();
         }
     };
 
--- a/jmh-core/src/test/java/org/openjdk/jmh/runner/options/TestOptions.java	Mon Jan 20 14:44:17 2014 +0400
+++ b/jmh-core/src/test/java/org/openjdk/jmh/runner/options/TestOptions.java	Mon Jan 20 20:04:34 2014 +0400
@@ -432,18 +432,6 @@
     }
 
     @Test
-    public void testJvmClasspath() throws Exception {
-        CommandLineOptions cmdLine = new CommandLineOptions("--jvmClasspath", "sample.jar");
-        Options builder = new OptionsBuilder().jvmClasspath("sample.jar").build();
-        Assert.assertEquals(builder.getJvmClassPath(), cmdLine.getJvmClassPath());
-    }
-
-    @Test
-    public void testJvmClasspath_Default() throws Exception {
-        Assert.assertEquals(EMPTY_BUILDER.getJvmClassPath(), EMPTY_CMDLINE.getJvmClassPath());
-    }
-
-    @Test
     public void testJvm() throws Exception {
         CommandLineOptions cmdLine = new CommandLineOptions("--jvm", "sample.jar");
         Options builder = new OptionsBuilder().jvm("sample.jar").build();