JVM解析参数调试(以-XX:+UseParallelGC为例)

在hotspot/src/share/vm/runtime/globals.hpp以及各组件、平台相关的*_gloabals.hpp有这些VM参数的声明,然后在对应的cpp文件里有它们的实现。

  product(bool, UseParallelGC, false,                                       \
          "Use the Parallel Scavenge garbage collector")  

在product build中,被宏展开之后实际上会变成:

extern "C" bool UseParallelGC; // from DECLARE_PRODUCT_FLAG  
  
// globals.cpp  
  
// define the VM flag  
bool UseParallelGC = false; // from MATERIALIZE_PRODUCT_FLAG  
  
// keep all VM flags in a table  
static Flag flagTable[] = {  
  // ...  
  { "bool", "UseParallelGC", &UseParallelGC, "{product}", DEFAULT }, // from RUNTIME_PRODUCT_FLAG_STRUCT  
  // ...  
  {0, NULL, NULL}  
};  
  
Flag* Flag::flags = flagTable;  
  
// globals_extension.hpp  
  
// the two enums below are used to index into the flags table  
typedef enum {  
 // ...  
 Flag_UseParallelGC  
 // ...  
} CommandLineFlag;  
  
typedef enum {  
 // ...  
 Flag_UseParallelGC_bool  
 // ...  
} CommandLineFlagWithType;  

在JavaMain()函数中会创建虚机:

int JNICALL
JavaMain(void * _args)
{
    ...
    /* Initialize the virtual machine */
    start = CounterGet();
    if (!InitializeJVM(&vm, &env, &ifn)) {
        JLI_ReportErrorMessage(JVM_ERROR1);
        exit(1);
    }
    ...
}

创建虚拟机的函数中,首先会进行虚拟机参数解析并设置好相应的全局变量的值,例如UseParallelGC。然后在init_globals()中就会根据UseParallelGC在创建堆设置好GC回收策略。

jint Threads::create_vm(JavaVMInitArgs* args, bool* canTryAgain) {
  ...

  // Parse arguments
  jint parse_result = Arguments::parse(args);

  ...
  // Initialize global modules
  jint status = init_globals();
  ...
}

1.解析参数

jint Arguments::parse_each_vm_init_arg(const JavaVMInitArgs* args, bool* patch_mod_javabase, Flag::Flags origin) {
  // iterate over arguments
  for (int index = 0; index < args->nOptions; index++) {
    const JavaVMOption* option = args->options + index;
    if (match_option(option, "-verbose", &tail)) {

    } else if (match_option(option, "-Xmn", &tail)) {

    } else if (match_option(option, "-Xms", &tail)) {

    } else if (match_option(option, "-Xmx", &tail) || match_option(option, "-XX:MaxHeapSize=", &tail)) {

    } else if (match_option(option, "-D", &tail)) {
    } else if (match_option(option, "-XX:", &tail)) { // -XX:xxxx
      // 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;
        }
      }
    }
  }
}

parse_each_vm_init_arg会遍历获取到的参数列表,然后根据各参数进行相应的设置和赋值。-XX:+UseParallelGC最后会调用process_argument(tail, args->ignoreUnrecognized, origin)进行设置。


Flag的数据结构(runtime/globas.hpp):

struct Flag {
  const char* _type;
  const char* _name;
  void* _addr;
  NOT_PRODUCT(const char* _doc;)
  Flags _flags;

  // points to all Flags static array
  static Flag* flags;

  // number of flags
  static size_t numFlags;
}
Flag::Error CommandLineFlags::boolAtPut(const char* name, size_t len, bool* value, Flag::Flags origin) {
  Flag* result = Flag::find_flag(name, len);
  return boolAtPut(result, value, origin);
}

flagTable这个Flag数组记录了VM参数的名字与存储位置(地址)之间的对应关系。于是通过它就可以实现从字符串到实际全局变量的赋值。

遍历flagTable找到名字所指定的Flag:

// Search the flag table for a named flag
Flag* Flag::find_flag(const char* name, size_t length, bool allow_locked, bool return_flag) {
  for (Flag* current = &flagTable[0]; current->_name != NULL; current++) {
    if (str_equal(current->_name, name, length)) {
      // Found a matching entry.
      // Don't report notproduct and develop flags in product builds.
      if (current->is_constant_in_binary()) {
        return (return_flag ? current : NULL);
      }
      // Report locked flags only if allowed.
      if (!(current->is_unlocked() || current->is_unlocker())) {
        if (!allow_locked) {
          // disable use of locked flags, e.g. diagnostic, experimental,
          // commercial... until they are explicitly unlocked
          return NULL;
        }
      }
      return current;
    }
  }
  // Flag name is not in the flag table
  return NULL;
}

然后通过Flag里记录的地址给VM参数对应的全局变量赋值:

Flag::Error CommandLineFlags::boolAtPut(Flag* flag, bool* value, Flag::Flags origin) {
  const char* name;
  if (flag == NULL) return Flag::INVALID_FLAG;
  if (!flag->is_bool()) return Flag::WRONG_FORMAT;
  name = flag->_name;
  Flag::Error check = apply_constraint_and_check_range_bool(name, *value, !CommandLineFlagConstraintList::validated_after_ergo());
  if (check != Flag::SUCCESS) return check;
  bool old_value = flag->get_bool();
  trace_flag_changed(name, old_value, *value, origin);
  check = flag->set_bool(*value);
  *value = old_value;
  flag->set_origin(origin);
  return check;
}

2.使用UseParallelGC参数

CollectedHeap* Universe::create_heap() {
  assert(_collectedHeap == NULL, "Heap already created");
#if !INCLUDE_ALL_GCS
  if (UseParallelGC) {
    fatal("UseParallelGC not supported in this VM.");
  } else if (UseG1GC) {
    fatal("UseG1GC not supported in this VM.");
  } else if (UseConcMarkSweepGC) {
    fatal("UseConcMarkSweepGC not supported in this VM.");
#else
  if (UseParallelGC) {
    return Universe::create_heap_with_policy();
  } else if (UseG1GC) {
    return Universe::create_heap_with_policy();
  } else if (UseConcMarkSweepGC) {
    return Universe::create_heap_with_policy();
#endif
  } else if (UseSerialGC) {
    return Universe::create_heap_with_policy();
  }

  ShouldNotReachHere();
  return NULL;
}

在Universe::create_heap()中会根据前面解析出来的堆相关参数,进而创建所需要的堆,并设置相应的GC回收策略。

3.附录

查找"JavaMain"在哪个文件中:
grep -rn "JavaMain" jdk/src/

root@wz-All-Series:/home/wz/openjdk/openjdk-jdk9-jdk9# grep -rn "JavaMain" jdk/src/
jdk/src/java.base/share/native/libjli/java.h:223:int JNICALL JavaMain(void * args); /* entry point                  */
jdk/src/java.base/share/native/libjli/java.c:386:JavaMain(void * _args)
jdk/src/java.base/share/native/libjli/java.c:2313:      rslt = ContinueInNewThread0(JavaMain, threadStackSize, (void*)&args);
jdk/src/java.base/macosx/native/libjli/java_md_macosx.c:1057:                rslt = JavaMain(&args)

查找"Threads::create_vm"方法在哪个文件中:
grep -rn "Threads::create_vm" hotspot/

root@wz-All-Series:/home/wz/openjdk/openjdk-jdk9-jdk9# grep -rn "Threads::create_vm" hotspot/
hotspot/src/share/vm/runtime/thread.cpp:3500:jint Threads::create_vm(JavaVMInitArgs* args, bool* canTryAgain) {

查找Arguments::parse

wz@wz-All-Series:~/openjdk/openjdk-jdk9-jdk9/hotspot$ grep -rn "Arguments::parse(" .
./src/share/vm/runtime/arguments.cpp:4311:jint Arguments::parse(const JavaVMInitArgs* initial_cmd_args) {

查找"Universe::initialize_heap"

root@wz-All-Series:/home/wz/openjdk/openjdk-jdk9-jdk9# grep -rn "Universe::initialize_heap" hotspot/
hotspot/src/share/vm/memory/universe.cpp:754:jint Universe::initialize_heap() {

参考

  • 求问JVM参数解析问题

你可能感兴趣的:(JVM解析参数调试(以-XX:+UseParallelGC为例))