java运行过程

 

1。java概念
2。JRE版本控制
3。JRE类库查找
4。Java的虚拟机启动和加载类库

 


 

1。java概念

Java
byte code (字节码)
jvm.dll (虚拟机)
JRE (运行环境)
JDK (开发框架)
package (类库) 

2。JRE版本控制

当在控制台执行java.exe,操作系统寻找JRE的方式如下:
1) 先找当前目录下有没有JRE
2)再找父目录下有没有JRE
3)接着在PATH路径中找JRE
4)注册表HKEY_LOCAL_MACHINE/SOFTWARE/JavaSoft/Java Runtime Environment/ 查看CurrentVersion的键值指向哪个JRE

//Windows平台java.exe调用jre过程//
===========================================
查找jre路径是通过java_md.c中函数:GetJREPath实现的。

<1>该函数首先调用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路径。

 <2>如果上面两种情况都不存在,则从注册表中去查找(参见函数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”。

3。JRE类库查找

JRE自带的基础类库主要是JRE/lib/rt.jar这个文件,包括了Java2平台标准版的所有类库。和JRE的版本一致。

JRE中由ClassLoader负责查找和加载程序引用到的类库,基础类库ClassLoader会到rt.jar中自动加载,其它的类库,ClassLoader在环境变量CLASSPATH指定的路径中搜索,按照先来先到的原则,放在CLASSPATH前面的类库先被搜到,Java程序启动之前建议先把PATH和CLASSPATH环境变量设好,OS通过PATH来找JRE,确定基础类库rt.jar的位置,JRE的ClassLoader通过CLASSPATH找其它类库。但有时候会出现这样的情况,希望替换基础类库中的类库,那么也可以简单的通过-Djava.endrosed.path=...参数传递给java.exe,于是ClassLoader会先于基础类库使用java.endrosed.path参数指定路径的类库。因此Java的版本管理是非常简单有效的,也许很原始,不过很好用,简单就不容易出错。

4。Java的虚拟机启动和加载类库

在Console执行java.exe xxx命令以后,如前所述的寻找JRE,OS找到JRE目录,根据java.exe的传递参数,选择加载Server版的jvm.dll还是Client版的jvm.dll,然后加载jvm.dll,把控制权交给jvm.dll。

接下来,jvm.dll进行初始化,分配内存等等动作,然后在CLASSPATH路径中寻找class,找到class以后,寻找class中的程序入口点Main函数,然后从Main函数执行程序,在执行过程中,使用ClassLoader动态加载一系列引用到的类。当调用到native方法时,jvm.dll告诉OS在JRE/bin目录下寻找某某DLL文件,调入内存,于是实现了JNI调用。

//Windows平台java.exe启动jvm.dll的过程//
===========================================
<1>装载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”。文件内容如下:

<3>取jvm.dll文件路径是通过java_md.c中函数:GetJVMPath实现的。

由上面两步我们已经获得了JRE路径和jvm的类型字符串。GetJVMPath函数判断CheckJvmType返回的jvm类型字符串中是否包含了‘/’或‘/’如果包含则以该jvm类型字符串+/jvm.dll作为JVM的全路径,否则以JRE路径+/bin+/jvm类型字符串+/jvm.dll作为JVM的全路径。

下面是3个例子:

看看上面的例子,

第一种情况“java -J-client test”jvm.dll路径为:JRE路径+/bin+/jvm类型字符串+/jvm.dll 按照我的JDK路径则为:“D:/java/j2sdk1.4.2_04/jre”+“/bin”+“/client”+“/jvm.dll”。

第二种情况“java -Xaltjvm=D:/java/j2sdk1.4.2_04/jre/bin/client test”路径为:jvm类型字符串+/jvm.dll即为:“:/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:/java/j2sdk1.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----

    JRE path is D:/java/j2sdk1.4.2_04/jre

    jvm.cfg[0] = ->-client<-

    jvm.cfg[1] = ->-server<-

    jvm.cfg[2] = ->-hotspot<-

    jvm.cfg[3] = ->-classic<-

    jvm.cfg[4] = ->-native<-

    jvm.cfg[5] = ->-green<-

    299 micro seconds to parse jvm.cfg

    JVM path is D:/java/j2sdk1.4.2_04/jre/bin/client/jvm.dll

    2897 micro seconds to LoadJavaVM

    JavaVM args:

        version 0x00010002, ignoreUnrecognized is JNI_FALSE, nOptions is 2

        option[ 0] = '-Djava.class.path=.'

        option[ 1] = '-Dsun.java.command=test.ScanDirectory'

    50001 micro seconds to InitializeJVM

    Main-Class is 'test.ScanDirectory'

    Apps' argc is 0

    10208 micro seconds to load main class

    ----_JAVA_LAUNCHER_DEBUG----

    usage: java test.ScanDirectory DIR [output file]

情况二

    E:/work/java_research>java -XXaltjvm=D:/java/j2sdk1.4.2_04/jre/bin/client test.ScanDirectory

    ----_JAVA_LAUNCHER_DEBUG----

    JRE path is D:/java/j2sdk1.4.2_04/jre

    jvm.cfg[0] = ->-client<-

    jvm.cfg[1] = ->-server<-

    jvm.cfg[2] = ->-hotspot<-

    jvm.cfg[3] = ->-classic<-

    jvm.cfg[4] = ->-native<-

    jvm.cfg[5] = ->-green<-

    386 micro seconds to parse jvm.cfg

    JVM path is D:/java/j2sdk1.4.2_04/jre/bin/client/jvm.dll

    2795 micro seconds to LoadJavaVM

    JavaVM args:

        version 0x00010002, ignoreUnrecognized is JNI_FALSE, nOptions is 2

        option[ 0] = '-Djava.class.path=.'

        option[ 1] = '-Dsun.java.command=test.ScanDirectory'

    49978 micro seconds to InitializeJVM

    Main-Class is 'test.ScanDirectory'

    Apps' argc is 0

    9598 micro seconds to load main class

    ----_JAVA_LAUNCHER_DEBUG----

    usage: java test.ScanDirectory DIR [output file]

情况三

    E:/work/java_research>java test.ScanDirectory

    ----_JAVA_LAUNCHER_DEBUG----

    JRE path is D:/java/j2sdk1.4.2_04/jre

    jvm.cfg[0] = ->-client<-

    jvm.cfg[1] = ->-server<-

    jvm.cfg[2] = ->-hotspot<-

    jvm.cfg[3] = ->-classic<-

    jvm.cfg[4] = ->-native<-

    jvm.cfg[5] = ->-green<-

    381 micro seconds to parse jvm.cfg

    JVM path is D:/java/j2sdk1.4.2_04/jre/bin/client/jvm.dll

    3038 micro seconds to LoadJavaVM

    JavaVM args:

        version 0x00010002, ignoreUnrecognized is JNI_FALSE, nOptions is 2

        option[ 0] = '-Djava.class.path=.'

        option[ 1] = '-Dsun.java.command=test.ScanDirectory'

    50080 micro seconds to InitializeJVM

    Main-Class is 'test.ScanDirectory'

    Apps' argc is 0

    10215 micro seconds to load main class

    ----_JAVA_LAUNCHER_DEBUG----

    usage: java test.ScanDirectory DIR [output file]

    三个的JVM路径都为:

    JVM path is D:/java/j2sdk1.4.2_04/jre/bin/client/jvm.dll

其他情况

    E:/work/java_research>java -J-server test.ScanDirectory

    ----_JAVA_LAUNCHER_DEBUG----

    JRE path is D:/java/j2sdk1.4.2_04/jre

    jvm.cfg[0] = ->-client<-

    jvm.cfg[1] = ->-server<-

    jvm.cfg[2] = ->-hotspot<-

    jvm.cfg[3] = ->-classic<-

    jvm.cfg[4] = ->-native<-

    jvm.cfg[5] = ->-green<-

    377 micro seconds to parse jvm.cfg

    JVM path is D:/java/j2sdk1.4.2_04/jre/bin/server/jvm.dll

    2985 micro seconds to LoadJavaVM

    JavaVM args:

        version 0x00010002, ignoreUnrecognized is JNI_FALSE, nOptions is 2

        option[ 0] = '-Djava.class.path=.'

        option[ 1] = '-Dsun.java.command=test.ScanDirectory'

    62382 micro seconds to InitializeJVM

    Main-Class is 'test.ScanDirectory'

    Apps' argc is 0

    12413 micro seconds to load main class

    ----_JAVA_LAUNCHER_DEBUG----

    usage: java test.ScanDirectory DIR [output file]

    E:/work/java_research>java -XXaltjvm=D:/java/j2sdk1.4.2_04/jre/bin/server test.ScanDirectory

    ----_JAVA_LAUNCHER_DEBUG----

    JRE path is D:/java/j2sdk1.4.2_04/jre

    jvm.cfg[0] = ->-client<-

    jvm.cfg[1] = ->-server<-

    jvm.cfg[2] = ->-hotspot<-

    jvm.cfg[3] = ->-classic<-

    jvm.cfg[4] = ->-native<-

    jvm.cfg[5] = ->-green<-

    376 micro seconds to parse jvm.cfg

    JVM path is D:/java/j2sdk1.4.2_04/jre/bin/server/jvm.dll

    2937 micro seconds to LoadJavaVM

    JavaVM args:

        version 0x00010002, ignoreUnrecognized is JNI_FALSE, nOptions is 2

        option[ 0] = '-Djava.class.path=.'

        option[ 1] = '-Dsun.java.command=test.ScanDirectory'

    62725 micro seconds to InitializeJVM

    Main-Class is 'test.ScanDirectory'

    Apps' argc is 0

    8942 micro seconds to load main class

    ----_JAVA_LAUNCHER_DEBUG----

    usage: java test.ScanDirectory DIR [output file]

总结:

1。jre的查找可以通过path路径和java.exe文件

2。也可以查找注册表查找,如写入下列注册表文件即可。

Windows Registry Editor Version 5.00

   

    [HKEY_LOCAL_MACHINE/SOFTWARE/JavaSoft]

   

    [HKEY_LOCAL_MACHINE/SOFTWARE/JavaSoft/Java Runtime Environment]

    "CurrentVersion"="1.4"

   

    [HKEY_LOCAL_MACHINE/SOFTWARE/JavaSoft/Java Runtime Environment/1.4]

    "JavaHome"="D://java//j2sdk1.4.2_04//jre"

 

 

 

   #

    # @(#)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替代而直接抛出错误信息。

  <2> 在运行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名称,去掉名称前面的‘-’返回该值。heckJvmType函数的这个返回值会在下面的函数中汇同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函数。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值