HotspotVM的类型选择

[size=x-large][align=left]前言[/align][/size]
一般情况下,JDK中提供了Client和Server两种类型的JVM,那JDK是在运行时是如何选择的呢。本文主要探讨默认情况下Windows和Solaris(Linux)是如何选择JVM种类的。

[align=left][size=x-large]JVM.cfg文件[/size][/align]
在JRE_HOME/lib/(i386)/下存在这个一个jvm.cfg文件,用于配置JVM的种类。
/OpenJDK7/hotspot/src/share/tools/launcher/java.c 下的ReadKnownVMs函数注释详细的说明了该文件的主要格式:

/*
* jvmcfg := { vmLine }
1820 * vmLine := knownLine
1821 * | aliasLine
1822 * | warnLine
1823 * | ignoreLine
1824 * | errorLine
1825 * | predicateLine
1826 * | commentLine
1827 * knownLine := flag "KNOWN" EOL
1828 * warnLine := flag "WARN" EOL
1829 * ignoreLine := flag "IGNORE" EOL
1830 * errorLine := flag "ERROR" EOL
1831 * aliasLine := flag "ALIASED_TO" flag EOL
1832 * predicateLine := flag "IF_SERVER_CLASS" flag EOL
1833 * commentLine := "#" text EOL
1834 * flag := "-" identifier
*/



1.假如flag出现在knowLine行,那么identifier将作为存放JVM 库文件的路径名,去加载相应的VM;
2.假如flag出现在aliasLine行的第一个位置上,那么改行的第二个flag的identifier将作为JVM的名称;
3.假如flag出现在warnLine行的位置,那么该flag的identifier作为JVM的名称,但是会产生一条警告信息;

C:\Users\xingjl.fnst>D:\JDK\Oracle\jdk1.6.0_45\bin\java -classic -version
Warning: classic VM not supported; client VM will be used
java version "1.6.0_45"
Java(TM) SE Runtime Environment (build 1.6.0_45-b06)
Java HotSpot(TM) Client VM (build 20.45-b01, mixed mode)

4.假如flag出现在ignoreLine行的位置,那么该identifier将被忽略使用默认的VM。
5.假如flag出现在errorLine行的位置,那么错误消息将会生成。

C:\Users\xingjl.fnst>D:\JDK\Oracle\jdk1.6.0_45\bin\java -error -version
Unrecognized option: -error
Could not create the Java virtual machine.

6.假如flag出现在predicateLine行,并且通过了机器上的判断(是否为服务器),那么predicateLine行的第二个flag的identifier将作为vm的名称;否则使用第一个。
7.如果在命令行中没有flag指定,那么jvm.cfg的文件的第一行将作为VM的名称。
8.PredicateLines这一行只可以作为jvm,cfg文件的第一行使用。
下面一个Windows JDK6u45的jvm.cfg文件的内容

-client KNOWN
-server KNOWN
-hotspot ALIASED_TO -client
-classic WARN
-native ERROR
-green ERROR

[align=left][size=x-large]Lanuger启动流程[/size][/align]
首先java.exe的CreateExecutionEnvironment函数调用GetJREPath去分析JRE的的安装路径,然后调用ReadKnownVMs函数去加载jvm.cfg文件,将文件中内容加载到如下的结构体中:

/* Values for vmdesc.flag */
#define VM_UNKNOWN -1
#define VM_KNOWN 0
#define VM_ALIASED_TO 1
#define VM_WARN 2
#define VM_ERROR 3
#define VM_IF_SERVER_CLASS 4
#define VM_IGNORE 5
struct vmdesc {
char *name;
int flag;
char *alias;
char *server_class;
};

其中flag为上面宏定义中定义的内容,name为jvm.cfg 文件中每行的第一个flag,当一行是aliasLine行时,aliasLine 则记录第二个falg的值;当一行为predicateLine 行时,server_class则记录第二flag的值。

加载完成后,调用CheckJvmType函数来确定当前所使用的VM种类;
[align=left][size=large]命令行指定VM类型[/size][/align]
1.命令行中,使用的是VM_KNOWN类型的(-client或-server),那么JVM将按照相应的路径加载jvm.dll
[table]
|-client|${ JAVA_HOME } \jre\bin\client\jvm.dll|
|-server|${ JAVA_HOME } \jre\bin\server\jvm.dll|
[/table]
2.在命令行中,使用的是VM_ALIASED_TO(别名)类型的第一个flag时(-hotspot),那么使用VM_ALIASED_TO行的第二个flag作为vm的种类

while (knownVMs[jvmidx].flag == VM_ALIASED_TO) {
int nextIdx = KnownVMIndex(knownVMs[jvmidx].alias);
–中略-
jvmidx = nextIdx;
jvmtype = knownVMs[jvmidx].name+1;
loopCount++;
}

3.在命令行中,使用的是VM_WARN行内容,则先输出一条警告消息,然后使用jvm.cfg文件中的第一行falg作为VM类型
4.在命令行中,使用的是VM_IGNORE行内容,使用jvm.cfg文件中的第一行falg作为VM类型
5.在命令行中,使用的是VM_ERROR行内容,则会打印Error消息。
以下是3-5条的代码:

switch (knownVMs[jvmidx].flag) {
case VM_WARN:
if (!speculative) {
fprintf(stderr, "Warning: %s VM not supported; %s VM will be used\n",
jvmtype, knownVMs[0].name + 1);
}
/* fall through */
case VM_IGNORE:
jvmtype = knownVMs[jvmidx=0].name + 1;
/* fall through */
case VM_KNOWN:
break;
case VM_ERROR:
if (!speculative) {
ReportErrorMessage2("Error: %s VM not supported", jvmtype, JNI_TRUE);
exit(1);
} else {
return "ERROR";
}
}

[align=left][size=large]命令行未指定VM类型[/size][/align]
在CheckJvmType函数中有如下代码:

if (jvmtype == NULL) {
char* result = knownVMs[0].name+1;
/* Use a different VM type if we are on a server class machine? */
if ((knownVMs[0].flag == VM_IF_SERVER_CLASS) &&
(ServerClassMachine() == JNI_TRUE)) {
result = knownVMs[0].server_class+1;
}
if (_launcher_debug) {
printf("Default VM: %s\n", result);
}
return result;
}

由此可知,VM类型默认使用jvm.cfg第一行的flag。最终由该行的类型(是否为VM_IF_SERVER_CLASS)以及是否为服务器决定VM的类型。
[align=left][size=medium]Windows下:[/size][/align]
JDK6u45的jvm.cfg文件参照上面提供的。
第一行非VM_IF_SERVER_CLASS类型,如果人为的修改为VM_IF_SERVER_CLASS类型,进行第二步的判断:是否为服务器级别的机器,Windows环境下的ServerClassMachine函数如下:

824jboolean
825ServerClassMachine() {
826 jboolean result = JNI_FALSE;
827#if defined(NEVER_ACT_AS_SERVER_CLASS_MACHINE)
828 result = JNI_FALSE;
829#elif defined(ALWAYS_ACT_AS_SERVER_CLASS_MACHINE)
830 result = JNI_TRUE;
831#endif
832 return result;
833}

一般情况下总是返回false,所以,即使修改了cfg文件,VM种类也不会根据机器的配置进行自动搭配。
[align=left][size=medium]Solaris(Linux)[/size][/align]
JDK6u45的jvm.cfg文件如下:

-client IF_SERVER_CLASS -server
-server KNOWN
-hotspot ALIASED_TO -client
-classic WARN
-native ERROR
-green ERROR

ServerClassMachine函数如下:

jboolean
1457ServerClassMachine(void) {
1458 jboolean result = JNI_FALSE;
1459#if defined(NEVER_ACT_AS_SERVER_CLASS_MACHINE)
1460 result = JNI_FALSE;
1461#elif defined(ALWAYS_ACT_AS_SERVER_CLASS_MACHINE)
1462 result = JNI_TRUE;
1463#elif defined(__sun) && defined(__sparc)
1464 result = solaris_sparc_ServerClassMachine();
1465#elif defined(__sun) && defined(i586)
1466 result = solaris_i586_ServerClassMachine();
1467#elif defined(__linux__) && defined(i586)
1468 result = linux_i586_ServerClassMachine();
1469#else
1470 if (_launcher_debug) {
1471 printf("ServerClassMachine: returns default value of %s\n",
1472 (result == JNI_TRUE ? "true" : "false"));
1473 }
1474#endif
1475 return result;
1476}

我们以sun sparc平台的JDK为例,solaris_sparc_ServerClassMachine函数代码如下:

1102jboolean
1103solaris_sparc_ServerClassMachine(void) {
1104 jboolean result = JNI_FALSE;
1105 /* How big is a server class machine? */
1106 const unsigned long server_processors = 2UL;
1107 const uint64_t server_memory = 2UL * GB;
1108 const uint64_t actual_memory = physical_memory();
1109
1110 /* Is this a server class machine? */
1111 if (actual_memory >= server_memory) {
1112 const unsigned long actual_processors = physical_processors();
1113 if (actual_processors >= server_processors) {
1114 result = JNI_TRUE;
1115 }
1116 }
1117 if (_launcher_debug) {
1118 printf("solaris_" LIBARCHNAME "_ServerClassMachine: %s\n",
1119 (result == JNI_TRUE ? "JNI_TRUE" : "JNI_FALSE"));
1120 }
1121 return result;
1122}

如果机器的内存大于等于2GB,并且机器的CPU为2核或以上时,即判定为服务器级别的机器。
综合Widows和Solaris的分析,默认的VM种类选在如下:

1. 32位的JDK在Windows平台上,总是使用-client模式(即jvm.cfg文件中的第一个flag)
默认第一个flag

-client KNOWN

2. 32位的JDK在Solaris(Linux)机器上,根据机器是否为服务器级别的选择相应的VM;服务器级别的使用-sever,否则使用-client
jvm.cfg默认的第一个falg

-client IF_SERVER_CLASS -server

3. 64位的JDK在Windows,Solaris平台上只有-server一种VM类型,可以选择-client但是 会被忽略。

-client IGNORE

4. 64位的JDK在Linux平台上只有-server一种VM类型,选择-client会报错

-client ERROR



-以上-

你可能感兴趣的:(JVM,C/C++)