版本信息
jdk版本:jdk8u40
操作系统:Mac
System.getProperty 方法大家并不陌生,在各大框架源码中都能见到,项目中也能使用到,那么此篇文章将带你揭开System.getProperty方法底层实现。
System.getProperty 可以拿到当前系统属性,比如当前操作系统的属性、动态链接库位置、编码集、当前虚拟机的版本等等一系列系统属性。当然,你可以把它理解为整个系统上下文的一个存储数据的集合,你可以往里面set属性,任何地点get取出,并且线程安全。下面案例是展示了默认情况下所有的属性(所有的key都展示出来了,项目中如需使用,可以先遍历一次再寻找)
public static void main(String[] args) {
Properties properties = System.getProperties();
Iterator> iterator = properties.entrySet().iterator();
while (iterator.hasNext()){
Map.Entry
接下来直接看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;
}
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;
}