服务热线:13616026886

技术文档 欢迎使用技术文档,我们为你提供从新手到专业开发者的所有资源,你也可以通过它日益精进

位置:首页 > 技术文档 > JAVA > 新手入门 > 基础入门 > 查看文档

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 fcs
  community source release》,可从sun官方网站下载,主要分析的源代码为:
  j2se/src/share/bin/java.c
  j2se/src/windows/bin/java_md.c
  
  java.c是什么东西
  
  ‘java程序’源代码
  所谓‘java程序’,包括jdk中的java.exe/javac.exe/javadoc.exe,java.c源
  代码中通过java_args宏来控制生成的代码,如果该宏没定义则编译文件控制生
  成java.exe否则编译文件控制生成其他的‘java程序’。
  比如:
  j2se/make/java/javac/makefile(这是javac编译文件)中:
  $(cd) ../../sun/javac ; $(make) $@ release=$(release) full_version=$(full_version)
  j2se/make/sun/javac/javac/makefile(由上面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平台该函数的处
  理(j2se/src/windows/bin/java_md.c)。
  
  createexecutionenvironment函数主要分为三步处理:
  a、查找jre路径。
  b、装载jvm.cfg中指定的虚拟机动态连接库(jvm.dll)参数。
  c、取jvm.dll文件路径。
  
  实现:
  
  a、查找jre路径是通过java_md.c中函数:getjrepath实现的。
  该函数首先调用getapplicationhome函数,getapplicationhome函数调用windows
  api函数getmodulefilename取java.exe程序的绝对路径,以我的jdk安装路径为例,
  为:“d:/java/j2sdk1.4.2_04/bin/java.exe”,然后去掉文件名取绝对路径为:
  “d:/java/j2sdk1.4.2_04/bin”,之后会在去掉最后一级目录,现在绝对路径为:
  “d:/java/j2sdk1.4.2_04”。
  然后getjrepath函数继续判断刚刚取的路径+/bin/java.dll组合成的这个java.dll
  文件是否存在,如果存在则“d:/java/j2sdk1.4.2_04”为jre路径,否则判断取得
  的“d:/java/j2sdk1.4.2_04”路径+/jre/bin/java.dll文件是否存在,存在则
  “d:/java/j2sdk1.4.2_04/jre”为jre路径。如果上面两种情况都不存在,则从注
  册表中去查找(参见函数getpublicjrehome)。
  
  函数:getpublicjrehome先查找
  hkey_local_machine/software/javasoft/java runtime environment/currentversion
  键值“当前jre版本号”,判断“当前jre版本号”是否为1.4做为版本号,如果是则
  取hkey_local_machine/software/javasoft/java runtime environment/“当前jre版本号”
  /javahome的路径所在为jre路径。
  
  我的jdk返回的jre路径为:“d:/java/j2sdk1.4.2_04/jre”。
  
  b、装载jvm.cfg虚拟机动态连接库配置文件是通过java.c中函数:readknownvms实现
  的。
  该函数首先组合jvm.cfg文件的绝对路径,jre路径+/lib+/arch(cpu构架)+/jvm.cfg
  arch(cpu构架)的判断是通过java_md.c中getarch函数判断的,该函数中windows平
  台只有两种情况:win64的‘ia64’,其他情况都为‘i386’。我的为i386所以jvm.cfg
  文件绝对路径为:“d:/java/j2sdk1.4.2_04/jre/lib/i386/jvm.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=<jvm_dir>" 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:/java/j2sdk1.4.2_04/jre/bin/client”和“d:/java/j2sdk1.4.2_04/jre/bin/server”两个目录下都存在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<jvm.cfg中jvm名称>”、“java -xxaltjvm=<jvm类型名称>”或“java -j-xxaltjvm=<jvm类型名称>”。如果是第一种参数传递方式,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:/java/j2sdk1.4.2_04/jre/bin/client test”,则readknownvms
  会直接返回“d:/java/j2sdk1.4.2_04/jre/bin/client”;如果不带上面参数执行如:
  “java test”,因为在jvm.cfg配置文件中第一个存在的jvm为“-client”,所以函数
  readknownvms也会去掉jvm名称前的“-”返回“client”。其实这三中情况都是使用的
  “d:/java/j2sdk1.4.2_04/jre/bin/client/jvm.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 test”jvm.dll路径为:
  jre路径+/bin+/jvm类型字符串+/jvm.dll 按照我的jdk路径则为:
  “d:/java/j2sdk1.4.2_04/jre”+“/bin”+“/client”+“/jvm.dll”。
  第二种情况“java -xxaltjvm=d:/java/j2sdk1.4.2_04/jre/bin/client test”路径为:
  jvm类型字符串+/jvm.dll即为:“d:/java/j2sdk1.4.2_04/jre/bin/client”+“/jvm.dll”
  第三种情况“java test”为:“d:/java/j2sdk1.4.2_04/jre”+“/bin”+“/client”
  +“/jvm.dll”与情况一相同。所以这三种情况都是调用的jvm动态连接库“d:/javaj2sdk1.4.2_04/jre/bin/client/jvm.dll”处理test类的。
  
  我们来进一步验证一下:
  打开cmd控制台:
  
  设置java装载调试
  e:/work/java_research>set _java_launcher_debug=1
  
  情况一
  e:/work/java_research>java -j-client test.scandirectory
  ----_java_launcher_debug----

扫描关注微信公众号