src/share/vm/runtime/arguments.cpp

Print this page

        

@@ -54,10 +54,12 @@
 #endif // INCLUDE_ALL_GCS
 
 // Note: This is a special bug reporting site for the JVM
 #define DEFAULT_VENDOR_URL_BUG "http://bugreport.java.com/bugreport/crash.jsp"
 #define DEFAULT_JAVA_LAUNCHER  "generic"
+#define N_MAX_OPTIONS 64
+#define OPTION_BUFFER_SIZE 1024
 
 #define UNSUPPORTED_GC_OPTION(gc)                                     \
 do {                                                                  \
   if (gc) {                                                           \
     if (FLAG_IS_CMDLINE(gc)) {                                        \

@@ -110,11 +112,77 @@
 SystemProperty *Arguments::_java_home = NULL;
 SystemProperty *Arguments::_java_class_path = NULL;
 SystemProperty *Arguments::_sun_boot_class_path = NULL;
 
 char* Arguments::_ext_dirs = NULL;
+// change the 0 on next line to 1 to enable argument tracing
+#if 0
+#define JVM_ARGS_ETRACE(x) x
+#else
+#define JVM_ARGS_ETRACE(x)
+#endif
 
+// Dump VM Option with a tag
+void DumpOption(const JavaVMOption *option_p,
+                const int index, const char *tag) {
+  if (PrintVMOptions && Verbose) {
+    if (index >= 0) {
+      jio_fprintf(defaultStream::output_stream(),
+                  "%s:[%d]='%s'\n", tag, index, option_p->optionString);
+    }
+  }
+}
+
+// Dump VM Option with a tag
+void DumpVMOption(const JavaVMInitArgs* args_p,
+                  const int index, const char *tag) {
+  if (PrintVMOptions && Verbose) {
+    if (args_p->options != NULL) {
+      if ((index >= 0) && (index < args_p->nOptions)) {
+        JavaVMOption *option_p = args_p->options + index;
+        jio_fprintf(defaultStream::output_stream(),
+                    "%s:[%d]='%s'\n", tag, index, option_p->optionString);
+      }
+    }
+  }
+}
+
+// Dump VM Options with a tag
+void DumpVMOptions(const JavaVMInitArgs* args_p, const char *tag) {
+  if (PrintVMOptions && Verbose) {
+    if (args_p->options != NULL) {
+      for (int index = 0; index < args_p->nOptions; index++) {
+        JavaVMOption *option_p = args_p->options + index;
+        jio_fprintf(defaultStream::output_stream(),
+                    "%s:[%d]='%s'\n", tag, index, option_p->optionString);
+      }
+    }
+  }
+}
+
+// Free memory associated with an options list
+void FreeVMOptions(JavaVMInitArgs **args_pp, const JavaVMInitArgs *args_base_p) {
+  assert(args_pp != NULL, "args_pp must not be NULL");
+  assert(*args_pp != NULL, "*args_pp must not be NULL");
+  if (args_base_p == (*args_pp)) {
+    // Do not free memory we did not allocate
+    return;
+  }
+  assert((*args_pp)->options != NULL, "(*args_pp)->options must not be NULL");
+  for (int index = 0; index < (*args_pp)->nOptions; index++) {
+    JavaVMOption *option_p = (*args_pp)->options + index;
+    if (option_p->optionString != NULL) {
+      os::free(option_p->optionString);
+      option_p->optionString = NULL;
+    }
+  }
+  os::free((void *)(*args_pp)->options);
+  (*args_pp)->options = NULL;
+  os::free((void *)(*args_pp));
+  (*args_pp) = NULL;
+}
+
 // Check if head of 'option' matches 'name', and sets 'tail' to the remaining
 // part of the option string.
 static bool match_option(const JavaVMOption *option, const char* name,
                          const char** tail) {
   int len = (int)strlen(name);

@@ -2642,10 +2710,11 @@
                                        Flag::Flags origin) {
   // Remaining part of option string
   const char* tail;
 
   // iterate over arguments
+  DumpVMOptions(args, "Parse each");
   for (int index = 0; index < args->nOptions; index++) {
     bool is_absolute_path = false;  // for -agentpath vs -agentlib
 
     const JavaVMOption* option = args->options + index;
 

@@ -3275,12 +3344,14 @@
     } else if (match_option(option, "-XX:-CreateMinidumpOnCrash")) {
       FLAG_SET_CMDLINE(bool, CreateCoredumpOnCrash, false);
       jio_fprintf(defaultStream::output_stream(),
           "CreateMinidumpOnCrash is replaced by CreateCoredumpOnCrash: CreateCoredumpOnCrash is off\n");
     } else if (match_option(option, "-XX:", &tail)) { // -XX:xxxx
-      // Skip -XX:Flags= since that case has already been handled
-      if (strncmp(tail, "Flags=", strlen("Flags=")) != 0) {
+      // Skip -XX:Flags= and -XX:VMOptionsFile= since those cases have
+      // already been handled
+      if ((strncmp(tail, "Flags=", strlen("Flags=")) != 0) &&
+          (strncmp(tail, "VMOptionsFile=", strlen("VMOptionsFile=")) != 0)) {
         if (!process_argument(tail, args->ignoreUnrecognized, origin)) {
           return JNI_EINVAL;
         }
       }
     // Unknown option

@@ -3696,26 +3767,565 @@
 
   return false;
 }
 #endif // PRODUCT
 
-// Parse entry point called from JNI_CreateJavaVM
+jint Arguments::alloc_JVM_options_list(const int needed_slots,
+                                       const int version,
+                                       struct JavaVMInitArgs **ret_args_head) {
+  assert(needed_slots > 0, "needed_slots must greater than zero");
+  assert(ret_args_head != NULL, "ret_args_head must not be NULL");
 
-jint Arguments::parse(const JavaVMInitArgs* args) {
+  JVM_ARGS_ETRACE(jio_fprintf(defaultStream::error_stream(),
+                              "Allocating %d option slots\n", needed_slots));
+  JavaVMInitArgs *new_args_head =
+      (JavaVMInitArgs *) os::malloc(sizeof (JavaVMInitArgs), mtInternal);
+  if (new_args_head == NULL) {
+    jio_fprintf(defaultStream::error_stream(),
+                "Could not allocate arguments list head\n");
+    return JNI_ENOMEM;
+  }
 
-  // Remaining part of option string
-  const char* tail;
+  memset(new_args_head, 0, sizeof (struct JavaVMInitArgs));
 
-  // If flag "-XX:Flags=flags-file" is used it will be the first option to be processed.
+  int bytes_needed = needed_slots * (sizeof (struct JavaVMOption));
+  JVM_ARGS_ETRACE(jio_fprintf(defaultStream::error_stream(),
+                              "to allocate options list %d %d\n",
+                              needed_slots, bytes_needed));
+
+  JavaVMOption *new_options_head =
+      (struct JavaVMOption *) os::malloc(bytes_needed, mtInternal);
+
+  if (new_options_head == NULL) {
+    jio_fprintf(defaultStream::error_stream(),
+                "Could not allocate options list with %d entries"
+                " and %d bytes\n",
+                needed_slots, bytes_needed);
+
+    os::free(new_args_head);
+    new_args_head = NULL;
+
+    return JNI_ENOMEM;
+  }
+
+  memset(new_options_head, 0, bytes_needed);
+
+  JVM_ARGS_ETRACE(jio_fprintf(defaultStream::error_stream(),
+                              "allocation need for options list %d %d\n",
+                              needed_slots, bytes_needed));
+
+  // Assemble the args object
+  new_args_head->options = new_options_head;
+  new_args_head->nOptions = 0;
+  new_args_head->version = version;
+
+  // Return newly allocated but not populated options list
+  *ret_args_head = new_args_head;
+
+  return JNI_OK;
+}
+
+// Copy num_slots NULL separated JVM options from buffer to
+// new_options[0] through new_options[num_slots - 1].
+// The byte_cnt includes null termination.
+jint Arguments::copy_JVM_options_from_buf(const char *buffer,
+                                          const size_t byte_cnt,
+                                          const int num_slots,
+                                          int *cur_slot_p,
+                                          struct JavaVMOption *new_options) {
+
+  assert(buffer != NULL, "buffer must not be NULL");
+  assert(byte_cnt > 1, "byte_cnt must be greater than one");
+  assert(num_slots > 0, "num_slots must be greater than zero");
+  assert(cur_slot_p != NULL, "cur_slot_p must not be NULL");
+  assert(new_options != NULL, "new_options must not be NULL");
+
+  JVM_ARGS_ETRACE(jio_fprintf(defaultStream::error_stream(),
+                              "Copying %d options from buffer\n",
+                              num_slots));
+  (*cur_slot_p) = 0;
+  const char *head = buffer;
+  const char *tail = buffer + byte_cnt - 1;
+
+  JVM_ARGS_ETRACE(jio_fprintf(defaultStream::error_stream(),
+                              "Copying option %d %d %d\n",
+                              byte_cnt, *cur_slot_p, num_slots));
+
+  while ((head < tail) && (*cur_slot_p < num_slots)) {
+    // Skip null character separator(s)
+    while ((head < tail) && (*head == '\0')) {
+      head++;
+    }
+
+    if (head >= tail) {
+      break;
+    }
+
+    // save a copy of the string in the options list
+    new_options[(*cur_slot_p)].optionString =
+        os::strdup_check_oom(head, mtInternal);
+
+    if (new_options[(*cur_slot_p)].optionString == NULL) {
+      jio_fprintf(defaultStream::error_stream(),
+                  "Could not copy file option string '%s'\n", head);
+      return JNI_ENOMEM;
+    }
+
+    DumpOption(new_options, *cur_slot_p, "copy");
+    const char *dummy;
+    if (match_option(&new_options[(*cur_slot_p)],
+                     "-XX:VMOptionsFile=", &dummy)) {
+      jio_fprintf(defaultStream::error_stream(),
+                  "VM options file is only supported on the command line\n");
+
+      (*cur_slot_p)++;  // We do not need this slot, but count it on the
+                        // list so the error handling can clean this up
+      return JNI_EINVAL;
+    }
+
+    JVM_ARGS_ETRACE(jio_fprintf(defaultStream::error_stream(),
+                                "Copying option %d %d %s\n",
+                                *cur_slot_p,
+                                num_slots,
+                                new_options[(*cur_slot_p)].optionString));
+    (*cur_slot_p)++;  // Done with current slot
+
+    while ((head < tail) && (*head != '\0')) {
+      head++;
+    }
+  }
+
+  return JNI_OK;
+}
+
+// Parse the JVM options from file_name into ret_buff. The
+// options in ret_buff are NULL separated in order to preserve
+// any embedded white space that was quoted in file_name.
+jint Arguments::parse_JVM_options_file(const char *file_name,
+                                       char **ret_buff,
+                                       size_t *ret_bytes,
+                                       int *ret_opt_cnt) {
+
+  assert(file_name != NULL, "file_name must not be NULL.");
+  assert(ret_buff != NULL, "ret_buff must not be NULL.");
+  assert(ret_bytes != NULL, "ret_bytes must not be NULL.");
+  assert(ret_opt_cnt != NULL, "ret_opt_cnt must not be NULL.");
+
+  // Default all return params to not return data
+  *ret_buff = NULL;
+  *ret_bytes = 0;
+  *ret_opt_cnt = 0;
+
+  int fd = ::open(file_name, O_RDONLY);
+  if (fd < 0) {
+    jio_fprintf(defaultStream::error_stream(),
+                "Could not open options file '%s'\n",
+                file_name);
+    return JNI_ERR;
+  }
+
+  // '+ 1' for NULL termination even with max bytes
+  int bytes_alloc = OPTION_BUFFER_SIZE + 1;
+
+  char *buf = (char *)os::malloc(bytes_alloc, mtInternal);
+
+  if (buf == NULL) {
+    jio_fprintf(defaultStream::error_stream(),
+                "Could not allocate read buffer for options file parse\n");
+    os::close(fd);
+    return JNI_ENOMEM;
+  }
+
+  memset(buf, 0, (unsigned)bytes_alloc);
+
+  // Fill buffer
+  // Use ::read() instead of os::read because os::read()
+  // might do a thead state transition
+  // and it is too early for that here
+
+  int bytes_read = ::read(fd, (void *)buf,
+                          (unsigned)bytes_alloc);
+  if (bytes_read < 0) {
+    os::free(buf);
+    buf = NULL;
+    os::close(fd);
+    jio_fprintf(defaultStream::error_stream(),
+                "Could not read options file '%s'\n", file_name);
+    return JNI_ERR;
+  }
+
+  if (bytes_read == 0) {
+    // tell caller there is no option data and that is ok
+    os::free(buf);
+    buf = NULL;
+    os::close(fd);
+    return JNI_OK;
+  }
+
+  // file is larger than OPTION_BUFFER_SIZE
+  if (bytes_read > bytes_alloc - 1) {
+    os::free(buf);
+    buf = NULL;
+    os::close(fd);
+    jio_fprintf(defaultStream::error_stream(),
+                "Options file '%s' is larger than %d bytes.\n",
+                file_name, bytes_alloc - 1);
+    return JNI_EINVAL;
+  }
+
+  os::close(fd);
+
+  // some pointers to help with parsing
+  char *buf_end = buf + bytes_read;
+  char *cur_token_head = buf;
+  char *wrt = buf;
+  char *rd = buf;
+
+  // count tokens
+  int token_cnt = 0;
+
+  JVM_ARGS_ETRACE(jio_fprintf(defaultStream::error_stream(),
+                              "Parse buf loop > '%s'\n",
+                              buf));
+  // parse all options
+  while (rd < buf_end) {
+    JVM_ARGS_ETRACE(jio_fprintf(defaultStream::error_stream(),
+                                "Parse buf loop white > >%s<\n", rd));
+    // skip leading white space from the input string
+    while (rd < buf_end && iswhite(*rd)) {
+      rd++;
+    }
+
+    if (rd >= buf_end) {
+      break;
+    }
+
+    JVM_ARGS_ETRACE(jio_fprintf(defaultStream::error_stream(),
+                                "Parse next token > %s\n", rd));
+    // Remember this is where we found the head of the token.
+    cur_token_head = wrt;
+
+    // Tokens are strings of non white space characters separated
+    // by one or more white spaces.
+    while (rd < buf_end && !iswhite(*rd)) {
+      if (*rd == '\'' || *rd == '"') {      // handle a quoted string
+        int quote = *rd;                    // matching quote to look for
+        rd++;                               // don't copy open quote
+        while (rd < buf_end && *rd != quote) {
+                                            // include everything (even spaces)
+                                            // up until the close quote
+          *wrt++ = *rd++;                   // copy to option string
+        }
+
+        if (rd < buf_end) {
+          rd++;                             // don't copy close quote
+        } else {
+                                            // did not see closing quote
+          jio_fprintf(defaultStream::error_stream(),
+                      "Unmatched quote in '%s'\n", file_name);
+          os::free(buf);
+          buf = NULL;
+          return JNI_EINVAL;
+        }
+      } else {
+        *wrt++ = *rd++;                     // copy to option string
+      }
+    }
+
+    // steal a white space character and set it to NULL
+    *wrt++ = '\0';
+    token_cnt++;
+    // We now have a complete token
+    JVM_ARGS_ETRACE(jio_fprintf(defaultStream::error_stream(),
+                                "Current parsed token > %s\n",
+                                cur_token_head));
+
+    rd++;  // Advance to next character
+  }
+
+  JVM_ARGS_ETRACE(jio_fprintf(defaultStream::error_stream(),
+                              "Done parsing tokens > %d\n",
+                              token_cnt));
+
+  if (token_cnt > N_MAX_OPTIONS) {
+    jio_fprintf(defaultStream::error_stream(),
+                "Options file has more than %d options.\n",
+                N_MAX_OPTIONS);
+    os::free(buf);
+    buf = NULL;
+    return JNI_EINVAL;
+  }
+
+  // Pass the data the back to the caller
+  *ret_buff = buf;
+  *ret_opt_cnt = token_cnt;
+  *ret_bytes = (size_t)(wrt - buf);
+
+  return JNI_OK;
+}
+
+// Merge options from a JVM options file with the options provided
+// by argsin and return the merge results via *argsout. If the
+// '-XX:VMOptionsFile=...' option is not specified on the command
+// line, then there is nothing to merge and *argsout is set to NULL.
+jint Arguments::merge_JVM_options_file(const JavaVMInitArgs *argsin,
+                                       JavaVMInitArgs **argsout) {
+
+  assert(argsin != NULL, "argsin should not be NULL");
+  assert(argsout != NULL, "argsout should not be NULL");
+
+  JavaVMInitArgs *new_args_head = NULL;
+  char *options_file = NULL;
+  bool options_file_found = false;
+
+  JVM_ARGS_ETRACE(jio_fprintf(defaultStream::error_stream(),
+                              "Found %d existing option(s)\n",
+                              argsin->nOptions));
+
+  // Take a peak at options passed in, looking for an options file
+  int old_index = 0;
+  const char *tail = NULL;
+  while (old_index < argsin->nOptions) {
+    JavaVMOption *option = argsin->options + old_index;
+
+    JVM_ARGS_ETRACE(jio_fprintf(defaultStream::error_stream(),
+                                "Found existing option %s\n",
+                                option->optionString));
+
+    if (match_option(option, "-XX:VMOptionsFile=", &tail)) {
+      // Only one options file allowed on the command line.
+      if (options_file_found) {
+        jio_fprintf(defaultStream::error_stream(),
+                    "Only one VM Options file is supported "
+                    "on the command line\n");
+
+        *argsout = NULL;
+        os::free(options_file);
+        options_file = NULL;
+        return JNI_EINVAL;
+      } else {
+        options_file_found = true;
+      }
+      options_file = os::strdup_check_oom(tail, mtInternal);
+      if (options_file == NULL) {
+        jio_fprintf(defaultStream::error_stream(),
+                    "Could not copy options file path\n");
+
+        return JNI_ENOMEM;
+      }
+    }
+    old_index++;
+  }
+
+  // No option file specified so nothing more to do
+  if (options_file == NULL) {
+    *argsout = NULL;
+    return JNI_OK;
+  }
+
+  DumpVMOptions(argsin, "Premerge");
+
+  char *buff;            // Contents of the options file
+  size_t byte_cnt = 0;   // bytes in buffer
+  int file_slots = 0;    // # options found in file
+
+  // We have requested an options file, so we need to read it
+  // and do a simple white space parse on it.
+  jint result = parse_JVM_options_file(options_file,
+                                       &buff,
+                                       &byte_cnt,
+                                       &file_slots);
+  if (result != JNI_OK) {
+    // the option file processing failed
+    jio_fprintf(defaultStream::error_stream(),
+                "Options file '%s', parsing error\n",
+                options_file);
+
+    os::free(options_file);
+    options_file = NULL;
+    *argsout = NULL;
+    return result;
+  }
+
+  JVM_ARGS_ETRACE(jio_fprintf(defaultStream::error_stream(),
+                              "Found %d bytes and %d options\n",
+                              byte_cnt, file_slots));
+  // no options but that is ok
+  if (file_slots == 0) {
+    os::free(options_file);
+    options_file = NULL;
+    *argsout = NULL;
+    return JNI_OK;
+  }
+
+  // Combine cmd line slot count and the options file slots passed to us
+  // We need one less because the "-XX:VMOptionsFile" is not included
+  // in the new list. 
+  int num_slots = argsin->nOptions + file_slots - 1;
+
+  JVM_ARGS_ETRACE(jio_fprintf(defaultStream::error_stream(),
+                              "Allocate option list with %d slots, %d file,"
+                              " %d exist\n",
+                              num_slots, file_slots, argsin->nOptions));
+
+  // Allocate a new option list
+  result = Arguments::alloc_JVM_options_list(num_slots, argsin->version,
+                                             &new_args_head);
+  if (result != JNI_OK) {
+    // the option file processing failed
+    jio_fprintf(defaultStream::error_stream(),
+                "Memory error in options processing\n");
+    os::free(buff);
+    buff = NULL;
+    os::free(options_file);
+    options_file = NULL;
+    *argsout = NULL;
+    return result;
+  }
+
+  JVM_ARGS_ETRACE(jio_fprintf(defaultStream::error_stream(),
+                              "merge option list > %d %d\n",
+                              argsin->nOptions, num_slots));
+
+  old_index = 0;
+  while ((old_index < argsin->nOptions) &&
+         (new_args_head->nOptions < num_slots)) {
+
+    JavaVMOption *old_option_p = argsin->options + old_index;
+    JavaVMOption *new_option_p = new_args_head->options +
+                                 new_args_head->nOptions;
+
+    JVM_ARGS_ETRACE(jio_fprintf(defaultStream::error_stream(),
+                                "Merge option %s old %d new %d\n",
+                                old_option_p->optionString,
+                                old_index, new_args_head->nOptions));
+
+    // Allow the VM option tracing to come on early.
+    if (match_option(old_option_p, "-XX:+PrintVMOptions")) {
+      PrintVMOptions = true;
+    }
+
+    if (match_option(old_option_p, "-XX:VMOptionsFile=", &tail)) {
+      int file_slots_used = 0;
+      result = Arguments::copy_JVM_options_from_buf(buff,
+                                                    byte_cnt,
+                                                    file_slots,
+                                                    &file_slots_used,
+                                                    new_option_p);
+      // Update option count for success and failure.
+      // The initialized count is required to free the new args object
+      new_args_head->nOptions += file_slots_used;
+
+      if (result != JNI_OK) {
+        // the option file processing failed
+        jio_fprintf(defaultStream::error_stream(),
+                    "Options copy error '%s'\n",
+                    options_file);
+
+        os::free(buff);
+        buff = NULL;
+        os::free(options_file);
+        options_file = NULL;
+        FreeVMOptions(&new_args_head, argsin);
+        *argsout = NULL;
+        return result;
+      }
+
+      old_index++;  // Next slot in the input list
+
+      JVM_ARGS_ETRACE(jio_fprintf(defaultStream::error_stream(),
+                                  "old %d new %d file %d\n",
+                                  old_index,
+                                  new_args_head->nOptions,
+                                  file_slots));
+    } else {
+      // We only increment the index if the option preexisted.
+      // The options file copying increments the index too
+      new_option_p->optionString =
+          os::strdup_check_oom(old_option_p->optionString, mtInternal);
+
+      if (new_option_p->optionString == NULL) {
+        jio_fprintf(defaultStream::error_stream(),
+                    "Could not allocate command option string '%s'\n",
+                    old_option_p->optionString);
+
+        FreeVMOptions(&new_args_head, argsin);
+        *argsout = NULL;
+        os::free(buff);
+        buff = NULL;
+        os::free(options_file);
+        options_file = NULL;
+        return JNI_ENOMEM;
+      }
+
+      new_option_p->extraInfo = NULL;
+
+      DumpOption(old_option_p, old_index, "old");
+      DumpOption(new_option_p, new_args_head->nOptions, "new");
+
+      DumpVMOption(new_args_head, new_args_head->nOptions, "new");
+      DumpVMOption(argsin, old_index, "old");
+      old_index++;  // Next slot in the input list
+      new_args_head->nOptions++;  // Next slot in the new args list
+     }
+
+  }
+
+  os::free(buff);
+  buff = NULL;
+  os::free(options_file);
+  options_file = NULL;
+
+  JVM_ARGS_ETRACE(jio_fprintf(defaultStream::error_stream(),
+                              "trace final num ops %d\n",
+                              new_args_head->nOptions));
+
+  // We have a list of options that is ready to be returned to the caller.
+  *argsout = new_args_head;
+  DumpVMOptions(*argsout, "Postmerge");
+  return JNI_OK;
+}
+
+// Parse entry point called from JNI_CreateJavaVM
+
+jint Arguments::parse(const struct JavaVMInitArgs *argsin) {
   const char* hotspotrc = ".hotspotrc";
   bool settings_file_specified = false;
   bool needs_hotspotrc_warning = false;
 
+  // If flag "-XX:Flags=flags-file" is used it will be the
+  // first option to be processed.
   const char* flags_file;
   int index;
+  struct JavaVMInitArgs *args = NULL;
+
+  jint result = Arguments::merge_JVM_options_file(argsin, &args);
+  if (result != JNI_OK) {
+    // A more specific error message has been printed to the error stream
+    jio_fprintf(defaultStream::error_stream(),
+                "Options merge error, VM will exit\n");
+    vm_exit(1);
+  }
+
+  if (args == NULL) {
+    args = (JavaVMInitArgs *)argsin;
+    JVM_ARGS_ETRACE(jio_fprintf(defaultStream::error_stream(),
+                                "no file options merged\n"));
+  } else {
+    JVM_ARGS_ETRACE(jio_fprintf(defaultStream::error_stream(),
+                                "file options merged\n"));
+  }
+
+  DumpVMOptions(args, "Final Command Line");
+  const char* tail = NULL;
   for (index = 0; index < args->nOptions; index++) {
     const JavaVMOption *option = args->options + index;
+    if (match_option(option, "-XX:VMOptionsFile=", &tail)) {
+      // -XX:VMOptionsFile= can only appear here if the option file
+      // is empty or if there are no options to process, ignore it.
+      continue;
+    }
     if (ArgumentsExt::process_options(option)) {
       continue;
     }
     if (match_option(option, "-XX:Flags=", &tail)) {
       flags_file = tail;

@@ -3780,16 +4390,18 @@
   }
 
   // Parse specified settings file
   if (settings_file_specified) {
     if (!process_settings_file(flags_file, true, args->ignoreUnrecognized)) {
+      FreeVMOptions(&args, argsin);
       return JNI_EINVAL;
     }
   } else {
 #ifdef ASSERT
     // Parse default .hotspotrc settings file
     if (!process_settings_file(".hotspotrc", false, args->ignoreUnrecognized)) {
+      FreeVMOptions(&args, argsin);
       return JNI_EINVAL;
     }
 #else
     struct stat buf;
     if (os::stat(hotspotrc, &buf) == 0) {

@@ -3806,15 +4418,18 @@
       }
     }
   }
 
   // Parse JavaVMInitArgs structure passed in, as well as JAVA_TOOL_OPTIONS and _JAVA_OPTIONS
-  jint result = parse_vm_init_args(args);
+  result = parse_vm_init_args(args);
   if (result != JNI_OK) {
+    FreeVMOptions(&args, argsin);
     return result;
   }
 
+  FreeVMOptions(&args, argsin);
+
   // Call get_shared_archive_path() here, after possible SharedArchiveFile option got parsed.
   SharedArchivePath = get_shared_archive_path();
   if (SharedArchivePath == NULL) {
     return JNI_ENOMEM;
   }