设为首页 收藏本站
查看: 883|回复: 0

[经验分享] Jetty的启动 java -jar start.jar

[复制链接]

尚未签到

发表于 2017-2-27 09:04:55 | 显示全部楼层 |阅读模式
  Jetty的一个默认的简易启动方式是java -jar start.jar。那这个命令是如何工作的呢?

  下面转载一篇文章,描述了用-jar参数时,classpath是如何工作的(http://www.zeali.net/entry/15)

  <转载开始>

  当用java -jar
yourJarExe.jar来运行一个经过打包的应用程序的时候,你会发现如何设置-classpath参数应用程序都找不到相应的第三方类,报ClassNotFound错误。实际上这是由于当使用-jar参数运行的时候,java
VM会屏蔽所有的外部classpath,而只以本身yourJarExe.jar的内部class作为类的寻找范围。
  **解决方案**
  一 BootStrap class扩展方案
  Java 命令行提供了如何扩展bootStrap 级别class的简单方法.

-Xbootclasspath:     完全取代基本核心的Java
class 搜索路径.

                                   不常用,否则要重新写所有Java
核心class

-Xbootclasspath/a: 后缀在核心class搜索路径后面.常用!!

-Xbootclasspath/p:
前缀在核心class搜索路径前面.不常用,避免

                                   引起不必要的冲突.
  语法如下:

 (分隔符与classpath参数类似,unix使用:号,windows使用;号,这里以unix为例)
java
-Xbootclasspath/a:/usrhome/thirdlib.jar: -jar yourJarExe.jar



  二 extend class 扩展方案
  Java exten class
存放在{Java_home}\jre\lib\ext目录下.当调用Java时,对扩展class路径的搜索是自动的.总会搜索的.这样,解决的方案就很简单了,将所有要使用的第三方的jar包都复制到ext
目录下.
  三 User class扩展方案
  当使用-jar执行可执行Jar包时,JVM将Jar包所在目录设置为codebase目录,所有的class搜索都在这个目录下开始.所以如果使用了其他第三方的jar包,一个比较可以接受的可配置方案,就是利用jar包的Manifest扩展机制.

步骤如下:
  1.将需要的第三方的jar包,复制在同可执行jar所在的目录或某个子目录下. 比如:jar 包在 /usrhome/yourJarExe.jar
那么你可以把所有jar包复制到/usrhome目录下或/usrhome/lib 等类似的子目录下.
  2.修改Manifest 文件
  在Manifest.mf文件里加入如下行
  Class-Path:classes12.jar
lib/thirdlib.jar


  Class-Path 是可执行jar包运行依赖的关键词.详细内容可以参考 http://java.sun.com/docs/books/tutorial/deployment/jar/downman.html
 。要注意的是
Class-Path
只是作为你本地机器的CLASSPATH环境变量的一个缩写,也就是说用这个前缀表示在你的jar包执行机器上所有的CLASSPATH目录下寻找相应的第三方类/类库。你并不能通过
Class-Path
来加载位于你本身的jar包里面(或者网络上)的jar文件。因为从理论上来讲,你的jar发布包不应该再去包含其他的第三方类库(而应该通过使用说明来提醒用户去获取相应的支持类库)。如果由于特殊需要必须把其他的第三方类库(jar,
zip, class等)直接打包在你自己的jar包里面一起发布,你就必须通过实现自定义的ClassLoader来按照自己的意图加载这些第三方类库。
  

以上三种方法推荐第一种
,扩展性好,操作起来也最方便.

另外编写自己的ClassLoader,来动态载入class,是更加复杂和高级技术.限于篇幅,不赘述.有兴趣了解可以去google一下custom
classloader,或者参考我的另一篇日志:让classpath参数走开

  Java的安全机制随不同的JDK版本有不同的变化,会影响很多核心CLASS,比如Thread,所以很多大型商业软件,要求JDK的版本很严格.部分原因也在此.这也要求在发布自己编写的应用时候,不管大小,都要说明开发和测试的JDK版本.
  

本文所述方法测试基于j2sdk 1.4.2_04-b05
  ----------------------------------------------------------------------------------------------
  附:背景知识
  自JDK 1.2以后,JVM采用了委托(delegate)模式来载入class.采用这种设计的原因可以参考http://java.sun.com/docs/books/tutorial/ext/basics/load.html

  归纳来讲:是基于JVM sandbox(沙盒)安装模型上提供应用层的可定制的安全机制.
  

Java虚拟机(JVM)寻找Class的顺序
  1. Bootstrap classes
  属于Java 平台核心的class,比如java.lang.String等.及rt.jar等重要的核心级别的class.这是由JVM Bootstrap
class loader来载入的.一般是放置在{java_home}\jre\lib目录下
  2. Extension classes
  基于Java扩展机制,用来扩展Java核心功能模块.比如Java串口通讯模块comm.jar.一般放置在{Java_home}\jre\lib\ext目录下
  3. User classes
  开发人员或其他第三方开发的Java程序包.通过命令行的-classpath或-cp,或者通过设置CLASSPATH环境变量来引用.JVM通过放置在{java_home}\lib\tools.jar来寻找和调用用户级的class.常用的javac也是通过调用tools.jar来寻找用户指定的路径来编译Java源程序.这样就引出了User
class路径搜索的顺序或优先级别的问题.
  3.1 缺省值:调用Java或javawa的当前路径(.),是开发的class所存在的当前目录

 3.2
CLASSPATH环境变量设置的路径.如果设置了CLASSPATH,则CLASSPATH的值会覆盖缺省值

 3.3
执行Java的命令行-classpath或-cp的值,如果制定了这两个命令行参数之一,它的值会覆盖环境变量CLASSPATH的值

 3.4 -jar
选项:如果通过java -jar 来运行一个可执行的jar包,这当前jar包会覆盖上面所有的值.换句话说,-jar
后面所跟的jar包的优先级别最高,如果指定了-jar选项,所有环境变量和命令行制定的搜索路径都将被忽略.JVM
APPClassloader将只会以jar包为搜索范围.

有关可执行jar有许多相关的安全方面的描述,可以参考http://java.sun.com/docs/books/tutorial/jar/
来全面了解.
  这也是为什么应用程序打包成可执行的jar包后,不管你怎么设置classpath都不能引用到第三方jar包的东西了.
  <转载结束>
  start.jar里面的META-INF目录里面有个文件叫MANIFEST.MF,关于这个文件,也转载一篇介绍(http://tech.it168.com/jd/2008-07-07/200807071008995.shtml)
  <转载开始>
  【IT168
技术文档】

打开Java的JAR文件我们经常可以看到文件中包含着一个META-INF目录,这个目录下会有一些文件,其中必有一个MANIFEST.MF,这个文件描述了该Jar文件的很多信息,下面将详细介绍MANIFEST.MF文件的内容,先来看struts.jar中包含的MANIFEST.MF文件内容:


  Manifest

-

Version:

1.0

  Created

-

By: Apache Ant

1.5

.

1

  Extension

-

Name: Struts Framework   Specification

-

Title: Struts Framework   Specification

-

Vendor: Apache Software Foundation   Specification

-

Version:

1.1

  Implementation

-

Title: Struts Framework   Implementation

-

Vendor: Apache Software Foundation   Implementation

-

Vendor

-

Id: org.apache   Implementation

-

Version:

1.1

  Class

-

Path: commons

-

beanutils.jar commons

-

collections.jar





commons

-

digester.jar commons

-

logging.jar commons

-

validator.jar jakarta

-

oro.jar





struts

-

legacy.jar





  如果我们把MANIFEST中的配置信息进行分类,可以归纳出下面几个大类:
  一. 一般属性
  1. Manifest-Version
  用来定义manifest文件的版本,例如:Manifest-Version: 1.0
  2. Created-By
  声明该文件的生成者,一般该属性是由jar命令行工具生成的,例如:Created-By: Apache Ant 1.5.1
  3. Signature-Version
  定义jar文件的签名版本
  4. Class-Path
  应用程序或者类装载器使用该值来构建内部的类搜索路径
  二. 应用程序相关属性
  1. Main-Class
  定义jar文件的入口类,该类必须是一个可执行的类,一旦定义了该属性即可通过 java -jar x.jar来运行该jar文件。
  三. 小程序(Applet)相关属性
  1. Extendsion-List
  该属性指定了小程序需要的扩展信息列表,列表中的每个名字对应以下的属性
  2. -Extension-Name
  3. -Specification-Version
  4. -Implementation-Version
  5. -Implementation-Vendor-Id
  5. -Implementation-URL
  四. 扩展标识属性
  1. Extension-Name
  该属性定义了jar文件的标识,例如Extension-Name: Struts Framework
  五. 包扩展属性
  1. Implementation-Title 定义了扩展实现的标题
  2. Implementation-Version 定义扩展实现的版本
  3. Implementation-Vendor 定义扩展实现的组织
  4. Implementation-Vendor-Id 定义扩展实现的组织的标识
  5. Implementation-URL : 定义该扩展包的下载地址(URL)
  6. Specification-Title 定义扩展规范的标题
  7. Specification-Version 定义扩展规范的版本
  8. Specification-Vendor 声明了维护该规范的组织
  9. Sealed 定义jar文件是否封存,值可以是true或者false (这点我还不是很理解)
  六. 签名相关属性
  签名方面的属性我们可以来参照JavaMail所提供的mail.jar中的一段
  Name: javax/mail/Address.class
  Digest-Algorithms: SHA MD5
  SHA-Digest: AjR7RqnN//cdYGouxbd06mSVfI4=
  MD5-Digest: ZnTIQ2aQAtSNIOWXI1pQpw==
  这段内容定义类签名的类名、计算摘要的算法名以及对应的摘要内容(使用BASE64方法进行编码)
  七.自定义属性
  除了前面提到的一些属性外,你也可以在MANIFEST.MF中增加自己的属性以及响应的值,例如J2ME程序jar包中就可能包含着如下信息


  MicroEdition

-

Configuration: CLDC

-

1.0

  MIDlet

-

Name: J2ME_MOBBER Midlet Suite   MIDlet

-

Info

-

URL: http:
//
www.javayou.com/



  MIDlet

-

Icon:

/

icon.png   MIDlet

-

Vendor: Midlet Suite Vendor   MIDlet

-

1

: mobber,

/

icon.png,mobber   MIDlet

-

Version:

1.0

.

0

  MicroEdition

-

Profile: MIDP

-

1.0

  MIDlet

-

Description: Communicator





  关键在于我们怎么来读取这些信息呢?其实很简单,JDK给我们提供了用于处理这些信息的API,详细的信息请见java.util.jar包中,我们可以通过给JarFile传递一个jar文件的路径,然后调用JarFile的getManifest方法来获取Manifest信息。
  更详细关于JAR文件的规范请见
  http://java.sun.com/j2se/1.3/docs/guide/jar/jar.html
  中文说明
  http://www-900.ibm.com/developerWorks/cn/java/j-jar/
  <转载结束>

  虽然start.jar里面的Main类的注释也指明MANIFEST.MF里面应该制定Main为Main-Class,不过至少7.2.2这个版本里还是没有指定。那么这个start.jar到底是怎么找到main class的呢?



  这里再转载一篇讲解java启动查找main函数的文章(http://www.linuxfans.org/bbs/thread-122385-1-1.html)

  不过看完了,我还是没明白这个start.jar是怎么找到main class的。
  <转载开始>


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=&lt;jvm_dir&gt;"
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&lt;jvm.cfg中jvm名称&gt;”、“java
-XXaltjvm

=&lt;jvm类型名称&gt;”或“java
-J-XXaltjvm=&lt;jvm类型名称&gt;”。如果是第一种参数传递方

式,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:\java\

j2sdk1.4.2_04\jre\bin\client\jvm.dll”处理test类的。


我们来进一步验证一下:

打开cmd控制台:


设置java装载调试

E:\work\java_research&gt;set
_JAVA_LAUNCHER_DEBUG=1

情况一

E:\work\java_research&gt;java -J-client
test.ScanDirectory

----_JAVA_LAUNCHER_DEBUG----

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

jvm.cfg[0] =
-&gt;-client&lt;-

jvm.cfg[1] =
-&gt;-server&lt;-

jvm.cfg[2] =
-&gt;-hotspot&lt;-

jvm.cfg[3] =
-&gt;-classic&lt;-

jvm.cfg[4] =
-&gt;-native&lt;-

jvm.cfg[5] = -&gt;-green&lt;-

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&gt;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] =
-&gt;-client&lt;-

jvm.cfg[1] =
-&gt;-server&lt;-

jvm.cfg[2] =
-&gt;-hotspot&lt;-

jvm.cfg[3] =
-&gt;-classic&lt;-

jvm.cfg[4] =
-&gt;-native&lt;-

jvm.cfg[5] = -&gt;-green&lt;-

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&gt;java
test.ScanDirectory

----_JAVA_LAUNCHER_DEBUG----

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

jvm.cfg[0] =
-&gt;-client&lt;-

jvm.cfg[1] =
-&gt;-server&lt;-

jvm.cfg[2] =
-&gt;-hotspot&lt;-

jvm.cfg[3] =
-&gt;-classic&lt;-

jvm.cfg[4] =
-&gt;-native&lt;-

jvm.cfg[5] = -&gt;-green&lt;-

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&gt;java
-J-server test.ScanDirectory

----_JAVA_LAUNCHER_DEBUG----

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

jvm.cfg[0] =
-&gt;-client&lt;-

jvm.cfg[1] =
-&gt;-server&lt;-

jvm.cfg[2] =
-&gt;-hotspot&lt;-

jvm.cfg[3] =
-&gt;-classic&lt;-

jvm.cfg[4] =
-&gt;-native&lt;-

jvm.cfg[5] = -&gt;-green&lt;-

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&gt;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] =
-&gt;-client&lt;-

jvm.cfg[1] =
-&gt;-server&lt;-

jvm.cfg[2] =
-&gt;-hotspot&lt;-

jvm.cfg[3] =
-&gt;-classic&lt;-

jvm.cfg[4] =
-&gt;-native&lt;-

jvm.cfg[5] = -&gt;-green&lt;-

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]


由上面可以看出,如果我们安装了多个jdk或jre版本的话,使用“java
-XXaltjvm=”

可以通过绝对路径指定到其他版本的jvm.dll上去,至于能不能运行还有待测试。


我们下面回到java.c的main函数中看看上面找到的jvm.dll是如何装载挂接执行的。


该操作大致分为三步:

a、装载jvm.dll动态连接库。

b、初始化jvm.dll并挂接到JNIEnv(JNI调用接口)实例。

c、调用JNIEnv实例装载并处理class类。


实现:

a、装载jvm.dll动态连接库是由main函数调用java_md.c中LoadJavaVM函数实现的。

main函数首先构造了一个InvocationFunctions结构的局部变量,InvocationFunctions

结构有两个函数指针:

typedef
struct {

    CreateJavaVM_t CreateJavaVM;

    GetDefaultJavaVMInitArgs_t
GetDefaultJavaVMInitArgs;

} InvocationFunctions;

函数LoadJavaVM中先调用windows
API函数:LoadLibrary装载jvm.dll动态连接库,

之后将jvm.dll中的导出函数JNI_CreateJavaVM和JNI_GetDefaultJavaVMInitArgs

挂接到InvocationFunctions变量的CreateJavaVM和GetDefaultJavaVMInitArgs函数

指针变量上。jvm.dll的装载工作宣告完成。


b、初始化jvm.dll并挂接到JNIEnv(JNI调用接口)实例是通过java.c中函数:

InitializeJVM完成的。

main方法中首先定义了一个JNIEnv结构的指针,JNIEnv结构中定义了许多与装载class

类文件、查找类方法、调用类方法有关的函数指针变量。InitializeJVM会调用上面

以挂接jvm.dll中JNI_CreateJavaVM的InvocationFunctions结构变量的CreateJavaVM

方法,即调用jvm.dll中函数JNI_CreateJavaVM,该函数会将JNIEnv结构的实例返回到

main中的JNIEnv结构的指针上。这样main中的JNIEnv指针获取了JNIEnv实例后,就可以

开始对class文件进行处理了。


c、调用JNIEnv实例装载并处理class类。

a)如果是执行jar包。

如果执行的是一个jar包的话,main函数会调用java.c中的函数:GetMainClassName,

该函数使用JNIEnv实例构造并调用java类:java.util.jar.JarFile中方法getManifest()

并从返回的Manifest对象中取getAttributes("Main-Class")的值,即jar包中文件:

META-INF/MANIFEST.MF指定的Main-Class的主类名作为运行的主类。

之后main函数会调用java.c中LoadClass方法装载该主类(使用JNIEnv实例的FindClass)。

b)如果是执行class方法。

main函数直接调用java.c中LoadClass方法装载该类。


然后main函数调用JNIEnv实例的GetStaticMethodID方法查找装载的class主类中

“public static void main(String[]
args)”方法,并判断该方法是否为public方法,

然后调用JNIEnv实例的CallStaticVoidMethod方法调用该java类的main方法。


总结

由上面的代码分析可以看出几个问题。

a、为什么JDK和JRE不一定通过安装,直接拷到硬盘上,设置path环境变量就可以执行。

因为java运行获取jre路径的首选方法正是直接通过获取java.exe绝对路径来判断的,

如果通过修改注册表选项而不设置path环境变量也可以找到jre路径所在。修改方法如下:

首先我们将java.exe拷到任意目录下,我的拷到e:\temp下,在cmd中运行:

清空path环境变量

E:\temp&gt;set
path=

E:\temp&gt;java

Error opening registry key
'Software\JavaSoft\Java Runtime Environment'

Error: could not find
java.dll

Error: could not find Java 2 Runtime
Environment.


导入如下注册表文件(java.reg)

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"


再执行显示执行正常,如下:

E:\temp&gt;java

Usage:
java [-options] class [args...]

           (to execute a class)

 
 or  java [-options] -jar jarfile [args...]

           (to execute a jar
file)


where options include:

    -client       to select the "client"
VM

    -server       to select the "server" VM

    -hotspot      is a
synonym for the "client" VM  [deprecated]

                  The default VM is
client.


    -cp &lt;class search path of directories and zip/jar
files&gt;

    -classpath &lt;class search path of directories and
zip/jar files&gt;

                  A ; separated list of directories,
JAR archives,

                  and ZIP archives to search for class
files.

    -D&lt;name&gt;=&lt;value&gt;

                
 set a system property

    -verbose[:class|gc|jni]

                
 enable verbose output

    -version      print product version and exit

 
  -showversion  print product version and continue

    -? -help      print
this help message

    -X            print help on non-standard options

   
-ea[:&lt;packagename&gt;...|:&lt;classname&gt;]

   
-enableassertions[:&lt;packagename&gt;...|:&lt;classname&gt;]

 
                enable assertions

   
-da[:&lt;packagename&gt;...|:&lt;classname&gt;]

   
-disableassertions[:&lt;packagename&gt;...|:&lt;classname&gt;]

 
                disable assertions

    -esa | -enablesystemassertions

    
             enable system assertions

    -dsa |
-disablesystemassertions

                  disable system
assertions

b、java.exe是通过jvm.cfg文件或直接指定jvm.dll路径来装载执行java程序的。

见上面例子。

c、不同实现版本的jvm.dll必然存在一个名为:JNI_CreateJavaVM的导出函数,

java.exe正是通过调用该函数获得JNIEnv调用接口来装载执行class类的。这个

函数也是我们下一步研究java
vm实作技巧的研究出发点。

JNI_CreateJavaVM函数位于:hotspot\src\share\vm\prims\jni.cpp文件中。


又志

潭州王姓,爱读聊斋,不求甚解,待业于家,百无聊赖乃取源码以读之,会有所得,

则拾而记之,游手好闲,为同侪所不齿,性尤不改,独好破解,谙习此道,今拾掇

箧中,破解软件十之又九也,中有visual
slickedit10,与时所行破解版不同,行

tag
files之操作而无碍,时时自得,或曰国人非尽为阿斗也,然掣肘于法律所制,

不欲示人,或有百觅而不得者,可于某之邮箱wall_john@sohu.com得之,或有爱读

源码而无偕行者,可于某之QQ:5672618觅之。
  <转载结束>

运维网声明 1、欢迎大家加入本站运维交流群:群②:261659950 群⑤:202807635 群⑦870801961 群⑧679858003
2、本站所有主题由该帖子作者发表,该帖子作者与运维网享有帖子相关版权
3、所有作品的著作权均归原作者享有,请您和我们一样尊重他人的著作权等合法权益。如果您对作品感到满意,请购买正版
4、禁止制作、复制、发布和传播具有反动、淫秽、色情、暴力、凶杀等内容的信息,一经发现立即删除。若您因此触犯法律,一切后果自负,我们对此不承担任何责任
5、所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其内容的准确性、可靠性、正当性、安全性、合法性等负责,亦不承担任何法律责任
6、所有作品仅供您个人学习、研究或欣赏,不得用于商业或者其他用途,否则,一切后果均由您自己承担,我们对此不承担任何法律责任
7、如涉及侵犯版权等问题,请您及时通知我们,我们将立即采取措施予以解决
8、联系人Email:admin@iyunv.com 网址:www.yunweiku.com

所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其承担任何法律责任,如涉及侵犯版权等问题,请您及时通知我们,我们将立即处理,联系人Email:kefu@iyunv.com,QQ:1061981298 本贴地址:https://www.yunweiku.com/thread-347688-1-1.html 上篇帖子: 在Eclipse中调试Jetty应用的配置(XML配置文件方式) 下篇帖子: maven3 jetty jrebel 搭建可调式的开发环境
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

扫码加入运维网微信交流群X

扫码加入运维网微信交流群

扫描二维码加入运维网微信交流群,最新一手资源尽在官方微信交流群!快快加入我们吧...

扫描微信二维码查看详情

客服E-mail:kefu@iyunv.com 客服QQ:1061981298


QQ群⑦:运维网交流群⑦ QQ群⑧:运维网交流群⑧ k8s群:运维网kubernetes交流群


提醒:禁止发布任何违反国家法律、法规的言论与图片等内容;本站内容均来自个人观点与网络等信息,非本站认同之观点.


本站大部分资源是网友从网上搜集分享而来,其版权均归原作者及其网站所有,我们尊重他人的合法权益,如有内容侵犯您的合法权益,请及时与我们联系进行核实删除!



合作伙伴: 青云cloud

快速回复 返回顶部 返回列表