JDK底层实现

    众所周知 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 官方网站下载,主分析的源代码为:

     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 函数调用 windowsAPI 函数 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.cfgARCH 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 ”和“ :\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 存在,而“ LIASED_TO ”表示为另一个 jvm.dll 的别名,“ WARN ”表示该虚拟机的 jvm.dll 不存在但运行时会用其他存在的 jvm.dll 替代执行,而“ ERROR ”同样表示该类虚拟机的 jvm.dll 不存在且运行时不会找存在的 jvm.dll 替代而直接抛出错误信息。

      在运行 java 程序时指定使用那个虚拟机的判断是由 java.c 中函数: CheckJvmType 判断,该函数会检查 java 运行参数中是否有指定 jvm 的参数,然后从 ReadKnownVMs 函数读取的

你可能感兴趣的:(java,jvm,虚拟机,windows,J2SE)