JVM源码剖析之System.getProperty实现

版本信息

jdk版本:jdk8u40
操作系统:Mac

System.getProperty 方法大家并不陌生,在各大框架源码中都能见到,项目中也能使用到,那么此篇文章将带你揭开System.getProperty方法底层实现。

System.getProperty 可以拿到当前系统属性,比如当前操作系统的属性、动态链接库位置、编码集、当前虚拟机的版本等等一系列系统属性。当然,你可以把它理解为整个系统上下文的一个存储数据的集合,你可以往里面set属性,任何地点get取出,并且线程安全。下面案例是展示了默认情况下所有的属性(所有的key都展示出来了,项目中如需使用,可以先遍历一次再寻找)

public static void main(String[] args) {

  Properties properties = System.getProperties();

  Iterator<Map.Entry<Object, Object>> iterator = properties.entrySet().iterator();

  while (iterator.hasNext()){
    Map.Entry<Object, Object> next = iterator.next();
    System.out.println("key:"+next.getKey()+";value:"+next.getValue());
  }
}

接下来直接看getProperty源码,实现非常非常非常非常的简单。在System类中维护了一个Properties类,直接从Properties中取出数据。而Properties实现了Hashtable,所以线程也是安全的。

public static String getProperty(String key) {

  …………
  
  return props.getProperty(key);
}

此时,我们更加关心Properties的数据来源,所以我们找到初始化Properties的地方。

private static void initializeSystemClass() {

  props = new Properties();
  initProperties(props);  

  …………

}

private static native Properties initProperties(Properties props);

而在Java层面找破头都找不到谁调用的initializeSystemClass方法,实际上这里是JVM初始化的过程中调用的此方法,所以肯定找不到,所以我们看到Hotspot中初始化的源码 src/share/vm/runtime/thread.cpp 文件中 create_vm 方法对于System类的初始化

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

  …………

  // 加载并初始化java_lang_System类。
  initialize_class(vmSymbols::java_lang_System(), CHECK_0);
  call_initializeSystemClass(CHECK_0);

  …………
}


static void call_initializeSystemClass(TRAPS) {
  // 拿到加载好的类对象(在Hotspot中Klass代表类)
  Klass* k =  SystemDictionary::resolve_or_fail(vmSymbols::java_lang_System(), true, CHECK);
  instanceKlassHandle klass (THREAD, k);

  JavaValue result(T_VOID);

  // 调用java_lang_System类中静态方法initializeSystemClass
  // 这恰好,也是我们在Java层面寻找不到的调用
  JavaCalls::call_static(&result, klass, vmSymbols::initializeSystemClass_name(),
                                         vmSymbols::void_method_signature(), CHECK);
}

既然initializeSystemClass方法的调用方我们找到了,此时就需要看到initProperties这个native方法对于Properties的初始化工作。

/src/share/native/java/lang/System.c 文件中对于initProperties这个native方法的实现。

JNIEXPORT jobject JNICALL
Java_java_lang_System_initProperties(JNIEnv *env, jclass cla, jobject props)
{
    char buf[128];

    // 获取到系统属性
    java_props_t *sprops = GetJavaProperties(env);

    // 获取到Properties的put方法签名
    jmethodID putID = (*env)->GetMethodID(env,
                                          (*env)->GetObjectClass(env, props),
                                          "put",
            "(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;");

    // 获取到Properties的remove方法签名
    jmethodID removeID = (*env)->GetMethodID(env,
                                          (*env)->GetObjectClass(env, props),
                                          "remove",
            "(Ljava/lang/Object;)Ljava/lang/Object;");

    // 获取到Properties的getProperty方法签名
    jmethodID getPropID = (*env)->GetMethodID(env,
                                          (*env)->GetObjectClass(env, props),
                                          "getProperty",
            "(Ljava/lang/String;)Ljava/lang/String;");

    // 调用Properties的put方法往Properties里面添加属性
    PUTPROP(props, "java.specification.version",
            JDK_MAJOR_VERSION "." JDK_MINOR_VERSION);
    PUTPROP(props, "java.specification.name",
            "Java Platform API Specification");
    PUTPROP(props, "os.name", sprops->os_name);
    PUTPROP(props, "os.version", sprops->os_version);
    PUTPROP(props, "os.arch", sprops->os_arch);

    …………

    // 把JVM启动时设置的参数添加
    // 可能是JVM自带的,也有可能是-D等等命令行设置的
    ret = JVM_InitProperties(env, props);

    …………

    return ret;
}
  1. 通过GetJavaProperties方法获取到系统属性
  2. initProperties这个native方法把Properties传入,然后通过PUTPROP宏往Properties里面添加各种系统属性
  3. 调用JVM_InitProperties方法,把JVM自带的,也有可能是-D等等命令行设置的系统属性添加到Properties中
JVM_ENTRY(jobject, JVM_InitProperties(JNIEnv *env, jobject properties))
  JVMWrapper("JVM_InitProperties");
  ResourceMark rm;

  Handle props(THREAD, JNIHandles::resolve_non_null(properties));

  // 把JVM的参数添加到Properties中
  for (SystemProperty* p = Arguments::system_properties(); p != NULL; p = p->next()) {
    PUTPROP(props, p->key(), p->value());
  }

  …………

  return properties;
JVM_END

static SystemProperty*  system_properties()   { return _system_properties; }

这里调用Arguments::system_properties()方法得到SystemProperty对象,SystemProperty对象存放JVM所有的系统参数,所以这里是在遍历JVM中所有的系统参数。最后,我们可以看一下这些JVM参数何时添加的。src/share/vm/runtime/arguments.cpp 文件中init_system_properties方法

void Arguments::init_system_properties() {

  // 添加到SystemProperty集合中
  PropertyList_add(&_system_properties, new SystemProperty("java.vm.specification.name",
                                                                 "Java Virtual Machine Specification",  false));
  PropertyList_add(&_system_properties, new SystemProperty("java.vm.version", VM_Version::vm_release(),  false));
  PropertyList_add(&_system_properties, new SystemProperty("java.vm.name", VM_Version::vm_name(),  false));
  PropertyList_add(&_system_properties, new SystemProperty("java.vm.info", VM_Version::vm_info_string(),  true));


  _java_ext_dirs = new SystemProperty("java.ext.dirs", NULL,  true);
  _java_endorsed_dirs = new SystemProperty("java.endorsed.dirs", NULL,  true);
  _sun_boot_library_path = new SystemProperty("sun.boot.library.path", NULL,  true);
  _java_library_path = new SystemProperty("java.library.path", NULL,  true);
  _java_home =  new SystemProperty("java.home", NULL,  true);
  _sun_boot_class_path = new SystemProperty("sun.boot.class.path", NULL,  true);

  _java_class_path = new SystemProperty("java.class.path", "",  true);

  // 添加到SystemProperty集合中
  PropertyList_add(&_system_properties, _java_ext_dirs);
  PropertyList_add(&_system_properties, _java_endorsed_dirs);
  PropertyList_add(&_system_properties, _sun_boot_library_path);
  PropertyList_add(&_system_properties, _java_library_path);
  PropertyList_add(&_system_properties, _java_home);
  PropertyList_add(&_system_properties, _java_class_path);
  PropertyList_add(&_system_properties, _sun_boot_class_path);

  os::init_system_properties_values();
}

还有在解析-D 等等java命令参数时也会添加,因为-D设置的属性是添加到System的Properties中

src/share/vm/runtime/arguments.cpp 文件中 parse_each_vm_init_arg方法

// 解析-D
else if (match_option(option, "-D", &tail)) {
  // 添加到SystemProperty集合中
  if (!add_property(tail)) {
    return JNI_ENOMEM;
  }
  …………
}

bool Arguments::add_property(const char* prop) {
  const char* eq = strchr(prop, '=');
  char* key;
  const static char ns[1] = {0};
  char* value = (char *)ns;

  // 解析key
  size_t key_len = (eq == NULL) ? strlen(prop) : (eq - prop);
  key = AllocateHeap(key_len + 1, mtInternal);
  strncpy(key, prop, key_len);
  key[key_len] = '\0';

  // 解析value
  // key和value用=号隔开
  if (eq != NULL) {
    size_t value_len = strlen(prop) - key_len - 1;
    value = AllocateHeap(value_len + 1, mtInternal);
    strncpy(value, &prop[key_len + 1], value_len + 1);
  }

  // 添加到SystemProperty集合中
  PropertyList_unique_add(&_system_properties, key, value);
  return true;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

程序员李哈

创作不易,希望能给与支持

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值