版本信息
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;
}
- 通过GetJavaProperties方法获取到系统属性
- initProperties这个native方法把Properties传入,然后通过PUTPROP宏往Properties里面添加各种系统属性
- 调用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_ENDstatic 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;// 解析keysize_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;
}