文章详情页
java源代码分析----jvm.dll装载过程
内容: 简述众所周知java.exe是java class文件的执行程序,但实际上java.exe程序只是一个执行的外壳,它会装载jvm.dll(windows下,以下皆以windows平台为例,linux下和solaris下其实类似,为:libjvm.so),这个动态连接库才是java虚拟机的实际操作处理所在。本文探究java.exe程序是如何查找和装载jvm.dll动态库,并调用它进行class文件执行处理的。源代码本文分析之代码,《JavaTM 2 SDK, Standard Edition, v1.4.2 fcsCommunity Source Release》,可从sun官方网站下载,主要分析的源代码为:j2sesrcsharebinjava.cj2sesrcwindowsbinjava_md.cjava.c是什么东西‘java程序’源代码所谓‘java程序’,包括jdk中的java.exejavac.exejavadoc.exe,java.c源代码中通过JAVA_ARGS宏来控制生成的代码,如果该宏没定义则编译文件控制生成java.exe否则编译文件控制生成其他的‘java程序’。比如:j2semakejavajavacMakefile(这是javac编译文件)中:$(CD) ../../sun/javac ; $(MAKE) $@ RELEASE=$(RELEASE) FULL_VERSION=$(FULL_VERSION)j2semakesunjavacjavacMakefile(由上面Makefile文件调用)中:JAVA_ARGS = '{ '-J-ms8m', 'com.sun.tools.javac.Main' }'则由同一份java.c代码生成的javac.exe程序就会直接调用java类方法:com.sun.tools.javac.Main,这样使其执行起来就像是直接运行的一个exe文件,而未定义JAVA_ARGS的java.exe程序则会调用传递过来参数中的类方法。从java.c的main入口函数说起main()函数中前面一段为重新分配参数指针的处理。然后调用函数:CreateExecutionEnvironment,该函数主要查找java运行环境的目录,和jvm.dll这个虚拟机核心动态连接库文件路径所在。根据操作系统不同,该函数有不同实现版本,但大体处理逻辑相同,我们看看windows平台该函数的处理(j2sesrcwindowsbinjava_md.c)。CreateExecutionEnvironment函数主要分为三步处理:a、查找jre路径。b、装载jvm.cfg中指定的虚拟机动态连接库(jvm.dll)参数。c、取jvm.dll文件路径。实现:a、查找jre路径是通过java_md.c中函数:GetJREPath实现的。该函数首先调用GetApplicationHome函数,GetApplicationHome函数调用windowsAPI函数GetModuleFileName取java.exe程序的绝对路径,以我的jdk安装路径为例,为:“D:javaj2sdk1.4.2_04binjava.exe,然后去掉文件名取绝对路径为:“D:javaj2sdk1.4.2_04bin,之后会在去掉最后一级目录,现在绝对路径为:“D:javaj2sdk1.4.2_04。然后GetJREPath函数继续判断刚刚取的路径+binjava.dll组合成的这个java.dll文件是否存在,如果存在则“D:javaj2sdk1.4.2_04为JRE路径,否则判断取得的“D:javaj2sdk1.4.2_04路径+jrebinjava.dll文件是否存在,存在则“D:javaj2sdk1.4.2_04jre为JRE路径。如果上面两种情况都不存在,则从注册表中去查找(参见函数GetPublicJREHome)。函数:GetPublicJREHome先查找HKEY_LOCAL_MACHINESoftwareJavaSoftJava Runtime EnvironmentCurrentVersion键值“当前JRE版本号,判断“当前JRE版本号是否为1.4做为版本号,如果是则取HKEY_LOCAL_MACHINESoftwareJavaSoftJava Runtime Environment“当前JRE版本号JavaHome的路径所在为JRE路径。我的JDK返回的JRE路径为:“D:javaj2sdk1.4.2_04jre。b、装载jvm.cfg虚拟机动态连接库配置文件是通过java.c中函数:ReadKnownVMs实现的。该函数首先组合jvm.cfg文件的绝对路径,JRE路径+lib+ARCH(CPU构架)+jvm.cfgARCH(CPU构架)的判断是通过java_md.c中GetArch函数判断的,该函数中windows平台只有两种情况:WIN64的‘ia64’,其他情况都为‘i386’。我的为i386所以jvm.cfg文件绝对路径为:“D:javaj2sdk1.4.2_04jrelibi386jvm.cfg。文件内容如下:## @(#)jvm.cfg 1.7 03/01/23# # Copyright 2003 Sun Microsystems, Inc. All rights reserved.# SUN PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.# # ### List of JVMs that can be used as an option to java, javac, etc.# Order is important -- first in this list is the default JVM.# NOTE that this both this file and its format are UNSUPPORTED and# WILL GO AWAY in a future release.## You may also select a JVM in an arbitrary location with the# '-XXaltjvm=' option, but that too is unsupported# and may not be available in a future release.#-client KNOWN-server KNOWN-hotspot ALIASED_TO -client-classic WARN-native ERROR-green ERROR(如果细心的话,我们会发现在JDK目录中我的为:“D:javaj2sdk1.4.2_04jrebinclient和“D:javaj2sdk1.4.2_04jrebinserver两个目录下都存在jvm.dll文件。而java正是通过jvm.cfg配置文件来管理这些不同版本的jvm.dll的。)ReadKnownVMs函数会将该文件中的配置内容读入到一个JVM配置结构的全局变量中,该函数首先跳过注释(以‘#’开始的行),然后读取以‘-’开始的行指定的jvm参数,每一行为一个jvm信息,第一部分为jvm虚拟机名称,第二部分为配置参数,比如行:“-client KNOWN则“-client为虚拟机名称,而“KNOWN为配置类型参数,“KNOWN表示该虚拟机的jvm.dll存在,而“ALIASED_TO表示为另一个jvm.dll的别名,“WARN表示该虚拟机的jvm.dll不存在但运行时会用其他存在的jvm.dll替代执行,而“ERROR同样表示该类虚拟机的jvm.dll不存在且运行时不会找存在的jvm.dll替代而直接抛出错误信息。在运行java程序时指定使用那个虚拟机的判断是由java.c中函数:CheckJvmType判断,该函数会检查java运行参数中是否有指定jvm的参数,然后从ReadKnownVMs函数读取的jvm.cfg数据结构中去查找,从而指定不同的jvm类型(最终导致装载不同jvm.dll)。有两种方法可以指定jvm类型,一种按照jvm.cfg文件中的jvm名称指定,第二种方法是直接指定,它们执行的方法分别是“java -J、“java -XXaltjvm=或“java -J-XXaltjvm=。如果是第一种参数传递方式,CheckJvmType函数会取参数‘-J’后面的jvm名称,然后从已知的jvm配置参数中查找如果找到同名的则去掉该jvm名称前的‘-’直接返回该值;而第二种方法,会直接返回“-XXaltjvm=或“-J-XXaltjvm=后面的jvm类型名称;如果在运行java时未指定上面两种方法中的任一一种参数,CheckJvmType会取配置文件中第一个配置中的jvm名称,去掉名称前面的‘-’返回该值。CheckJvmType函数的这个返回值会在下面的函数中汇同jre路径组合成jvm.dll的绝对路径。比如:如果在运行java程序时使用“java -J-client test则ReadKnownVMs会读取参数“-client然后查找jvm.cfg读入的参数中是否有jvm名称为“-client的,如果有则去掉jvm名称前的“-直接返回“client;而如果在运行java程序时使用如下参数:“java -XXaltjvm=D:javaj2sdk1.4.2_04jrebinclient test,则ReadKnownVMs会直接返回“D:javaj2sdk1.4.2_04jrebinclient;如果不带上面参数执行如:“java test,因为在jvm.cfg配置文件中第一个存在的jvm为“-client,所以函数ReadKnownVMs也会去掉jvm名称前的“-返回“client。其实这三中情况都是使用的“D:javaj2sdk1.4.2_04jrebinclientjvm.dll这个jvm动态连接库处理test这个class的,见下面GetJVMPath函数。c、取jvm.dll文件路径是通过java_md.c中函数:GetJVMPath实现的。由上面两步我们已经获得了JRE路径和jvm的类型字符串。GetJVMPath函数判断CheckJvmType返回的jvm类型字符串中是否包含了‘’或‘/’如果包含则以该jvm类型字符串+jvm.dll作为JVM的全路径,否则以JRE路径+bin+jvm类型字符串+jvm.dll作为JVM的全路径。看看上面的例子,第一种情况“java -J-client testjvm.dll路径为:JRE路径+bin+jvm类型字符串+jvm.dll 按照我的JDK路径则为:“D:javaj2sdk1.4.2_04jre+“bin+“client+“jvm.dll。第二种情况“java -XXaltjvm=D:javaj2sdk1.4.2_04jrebinclient test路径为:jvm类型字符串+jvm.dll即为:“D:javaj2sdk1.4.2_04jrebinclient+“jvm.dll第三种情况“java test为:“D:javaj2sdk1.4.2_04jre+“bin+“client+“jvm.dll与情况一相同。所以这三种情况都是调用的jvm动态连接库“D:javaj2sdk1.4.2_04jrebinclientjvm.dll处理test类的。我们来进一步验证一下:打开cmd控制台:设置java装载调试E:workjava_research>set _JAVA_LAUNCHER_DEBUG=1情况一E:workjava_research>java -J-client test.ScanDirectory----_JAVA_LAUNCHER_DEBUG----JRE path is D:javaj2sdk1.4.2_04jrejvm.cfg[0] = ->-client-server-hotspot-classic-native-greenjava test.ScanDirectory----_JAVA_LAUNCHER_DEBUG----JRE path is D:javaj2sdk1.4.2_04jrejvm.cfg[0] = ->-client-server-hotspot-classic-native-greenjava -XXaltjvm=D:javaj2sdk1.4.2_04jrebinserver test.ScanDirectory----_JAVA_LAUNCHER_DEBUG----JRE path is D:javaj2sdk1.4.2_04jrejvm.cfg[0] = ->-client-server-hotspot-classic-native-green
标签:
Java
上一条:java虚拟机详述-第一章下一条:Java Socket编程
相关文章:
排行榜