java

位置:IT落伍者 >> java >> 浏览文章

java源代码分析----jvm.dll装载过程


发布日期:2018年05月01日
 
java源代码分析----jvm.dll装载过程

简述

众所周知javaexe是java class文件的执行程序但实际上javaexe程序只是

一个执行的外壳它会装载jvmdll(windows下以下皆以windows平台为例

linux下和solaris下其实类似libjvmso)这个动态连接库才是java

虚拟机的实际操作处理所在本文探究javaexe程序是如何查找和装载jvmdll

动态库并调用它进行class文件执行处理的

源代码

本文分析之代码《JavaTM SDK Standard Edition v fcs

Community Source Release》可从sun官方网站下载主要分析的源代码为

jse\src\share\bin\javac

jse\src\windows\bin\java_mdc

javac是什么东西

java程序源代码

所谓java程序包括jdk中的javaexe\javacexe\javadocexejavac源

代码中通过JAVA_ARGS宏来控制生成的代码如果该宏没定义则编译文件控制生

成javaexe否则编译文件控制生成其他的java程序

比如

jse\make\java\javac\Makefile(这是javac编译文件)中

$(CD) //sun/javac ; $(MAKE) $@ RELEASE=$(RELEASE) FULL_VERSION=$(FULL_VERSION)

jse\make\sun\javac\javac\Makefile(由上面Makefile文件调用)中

JAVA_ARGS = { \Jmsm\ \comsuntoolsjavacMain\ }

则由同一份javac代码生成的javacexe程序就会直接调用java类方法

comsuntoolsjavacMain这样使其执行起来就像是直接运行的一个exe文件

而未定义JAVA_ARGS的javaexe程序则会调用传递过来参数中的类方法

从javac的main入口函数说起

main()函数中前面一段为重新分配参数指针的处理

然后调用函数CreateExecutionEnvironment该函数主要查找java运行环境的

目录和jvmdll这个虚拟机核心动态连接库文件路径所在根据操作系统不同

该函数有不同实现版本但大体处理逻辑相同我们看看windows平台该函数的处

理(jse\src\windows\bin\java_mdc)

CreateExecutionEnvironment函数主要分为三步处理

a查找jre路径

b装载jvmcfg中指定的虚拟机动态连接库(jvmdll)参数

c取jvmdll文件路径

实现

a查找jre路径是通过java_mdc中函数GetJREPath实现的

该函数首先调用GetApplicationHome函数GetApplicationHome函数调用windows

API函数GetModuleFileName取javaexe程序的绝对路径以我的jdk安装路径为例

D:\java\jsdk_\bin\javaexe然后去掉文件名取绝对路径为

D:\java\jsdk_\bin之后会在去掉最后一级目录现在绝对路径为

D:\java\jsdk_

然后GetJREPath函数继续判断刚刚取的路径+\bin\javadll组合成的这个javadll

文件是否存在如果存在则D:\java\jsdk_为JRE路径否则判断取得

D:\java\jsdk_路径+\jre\bin\javadll文件是否存在存在则

D:\java\jsdk_\jre为JRE路径如果上面两种情况都不存在则从注

册表中去查找(参见函数GetPublicJREHome)

函数GetPublicJREHome先查找

HKEY_LOCAL_MACHINE\Software\JavaSoft\Java Runtime Environment\CurrentVersion

键值当前JRE版本号判断当前JRE版本号是否为做为版本号如果是则

取HKEY_LOCAL_MACHINE\Software\JavaSoft\Java Runtime Environment\当前JRE版本号

\JavaHome的路径所在为JRE路径

我的JDK返回的JRE路径为D:\java\jsdk_\jre

b装载jvmcfg虚拟机动态连接库配置文件是通过javac中函数:ReadKnownVMs实现

该函数首先组合jvmcfg文件的绝对路径JRE路径+\lib+\ARCH(CPU构架)+\jvmcfg

ARCH(CPU构架)的判断是通过java_mdc中GetArch函数判断的该函数中windows平

台只有两种情况WINia其他情况都为i我的为i所以jvmcfg

文件绝对路径为D:\java\jsdk_\jre\lib\i\jvmcfg文件内容如

## @(#)jvmcfg //# # Copyright 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 KNOWNserver KNOWNhotspot ALIASED_TO clientclassic WARNnative ERRORgreen ERROR

(如果细心的话我们会发现在JDK目录中我的为D:\java\jsdk_\jre\bin\clientD:\java\jsdk_\jre\bin\server两个目录下都存在jvmdll文件而java正是通过jvmcfg配置文件来管理这些不同版本的jvmdll的

ReadKnownVMs函数会将该文件中的配置内容读入到一个JVM配置结构的全局变量中该函数首先跳过注释(以#开始的行)然后读取以开始的行指定的jvm参数每一行为一个jvm信息第一部分为jvm虚拟机名称第二部分为配置参数比如行

client KNOWNclient为虚拟机名称KNOWN为配置类型参数KNOWN

表示该虚拟机的jvmdll存在ALIASED_TO表示为另一个jvmdll的别名WARN

表示该虚拟机的jvmdll不存在但运行时会用其他存在的jvmdll替代执行ERROR

同样表示该类虚拟机的jvmdll不存在且运行时不会找存在的jvmdll替代而直接抛出错误

信息

在运行java程序时指定使用那个虚拟机的判断是由javac中函数CheckJvmType判断该函数会检查java运行参数中是否有指定jvm的参数然后从ReadKnownVMs函数读取的jvmcfg数据结构中去查找从而指定不同的jvm类型(最终导致装载不同jvmdll)有两种方法可以指定jvm类型一种按照jvmcfg文件中的jvm名称指定第二种方法是直接指定它们执行的方法分别是java J<jvmcfg中jvm名称>java XXaltjvm=<jvm类型名称>java JXXaltjvm=<jvm类型名称>如果是第一种参数传递方式CheckJvmType函数会取参数J后面的jvm名称然后从已知的jvm配置参数中查找如果找到同名的则去掉该jvm名称前的直接返回该值而第二种方法会直接返回XXaltjvm=JXXaltjvm=后面的jvm类型名称如果在运行java时未指定上面两种方法中的任一一种参数CheckJvmType会取配置文件中第一个配置中的jvm名称去掉名称前面的返回该值CheckJvmType函数的这个返回值会在下面的函数中汇同jre路径组合成jvmdll的绝对路径

比如如果在运行java程序时使用java Jclient test则ReadKnownVMs会读取参数client然后查找jvmcfg读入的参数中是否有jvm名称为client如果有则去掉jvm名称前的直接返回client而如果在运行java程序时使用如下参数

java XXaltjvm=D:\java\jsdk_\jre\bin\client test则ReadKnownVMs

会直接返回D:\java\jsdk_\jre\bin\client如果不带上面参数执行如

java test因为在jvmcfg配置文件中第一个存在的jvm为client所以函数

ReadKnownVMs也会去掉jvm名称前的返回client其实这三中情况都是使用的

D:\java\jsdk_\jre\bin\client\jvmdll这个jvm动态连接库处理test这个class的见下面GetJVMPath函数

c取jvmdll文件路径是通过java_mdc中函数GetJVMPath实现的

由上面两步我们已经获得了JRE路径和jvm的类型字符串GetJVMPath函数判断CheckJvmType

返回的jvm类型字符串中是否包含了\/如果包含则以该jvm类型字符串+\jvmdll作为JVM的全路径否则以JRE路径+\bin+\jvm类型字符串+\jvmdll作为JVM的全路径

看看上面的例子第一种情况java Jclient testjvmdll路径为

JRE路径+\bin+\jvm类型字符串+\jvmdll 按照我的JDK路径则为

D:\java\jsdk_\jre+\bin+\client+\jvmdll

第二种情况java XXaltjvm=D:\java\jsdk_\jre\bin\client test路径为

jvm类型字符串+\jvmdll即为D:\java\jsdk_\jre\bin\client+\jvmdll

第三种情况java testD:\java\jsdk_\jre+\bin+\client

+\jvmdll与情况一相同所以这三种情况都是调用的jvm动态连接库D:\javajsdk_\jre\bin\client\jvmdll处理test类的

我们来进一步验证一下

打开cmd控制台

设置java装载调试

E:\work\java_research>set _JAVA_LAUNCHER_DEBUG=

情况一

E:\work\java_research>java Jclient testScanDirectory

_JAVA_LAUNCHER_DEBUG

上一篇:Java中如何计算日期之间的天数

下一篇:Java虚拟机概念及体系结构详述