前言
一般情况下,JDK中提供了Client和Server两种类型的JVM,那JDK是在运行时是如何选择的呢。本文主要探讨默认情况下Windows和Solaris(Linux)是如何选择JVM种类的。
JVM.cfg文件
在JRE_HOME/lib/<arch>(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
Lanuger启动流程
首先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种类;
命令行指定VM类型
1.命令行中,使用的是VM_KNOWN类型的(-client或-server),那么JVM将按照相应的路径加载jvm.dll
-client | ${ JAVA_HOME } \jre\bin\client\jvm.dll |
-server | ${ JAVA_HOME } \jre\bin\server\jvm.dll |
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";
}
}
命令行未指定VM类型
在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的类型。
Windows下:
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种类也不会根据机器的配置进行自动搭配。
Solaris(Linux)
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
-以上-
分享到:
相关推荐
- **ClassVM**和**HotSpotVM**分别在J2SE和J2EE中执行`JavaApplet`、`JavaServlet`或`JavaApplication`,其中HotSpotVM因高效而著称。 #### PersonalJava PersonalJava是一种基于J2ME CDC的规范,特别适合于具备...
源自知乎的一个问题,对于Java来说,所有类的父类都是Object类,但是Object类本身又属于类类型,即Class类型,那么问题就来了。 3. JVM简史 源自周老师的文章,讲述了JVM虚拟机的发展史,以及各种各样的Java虚拟机 4...
源自知乎的一个问题,对于Java来说,所有类的父类都是Object类,但是Object类本身又属于类类型,即Class类型,那么问题就来了。 3. JVM简史 源自周老师的文章,讲述了JVM虚拟机的发展史,以及各种各样的Java虚拟机 4...
- **栈内存**:包括虚拟机栈和本地方法栈,用于支持Java方法和本地方法的执行,存放基本数据类型、对象引用等。 - **程序计数器**:用于指示当前线程所执行的字节码指令的位置,是“线程私有”的内存区域。 ### JVM...
JVM内存区域主要分为线程...线程私有数据区域生命周期与线程相同,依赖用户线程的启动/结束而创建/销毁(在HotspotVM内,每个线程都与操作系统的本地线程直接映射,因此这部分内存区域的存/否跟随本地线程的生/死对应)。
方法区(Method Area)则是各个线程共享的内存区域,用于存储类型信息、常量、静态变量等。运行时常量池(Runtime Constant Pool)作为方法区的一部分,存放了编译期生成的各种字面量与符号引用。 垃圾收集的过程...
第14节Java虚拟机-HotSpotVM00:04:23分钟 | 第15节Java虚拟机-kvm00:03:04分钟 | 第16节Java虚拟机-JRockit00:04:12分钟 | 第17节Java虚拟机-j900:04:23分钟 | 第18节Java虚拟机-dalvik00:02:20分钟 | 第19节...
第14讲 Java虚拟机-HotSpotVM 00:04:23 第15讲 Java虚拟机-kvm 00:03:04 第16讲 Java虚拟机-JRockit 00:04:12 第17讲 Java虚拟机-j9 00:04:23 第18讲 Java虚拟机-dalvik 00:02:20 第19讲 Java...