java中.class文件java class怎么打开开

JVM加载class文件的原理 - 爪哇人 - ITeye技术网站
博客分类:
当Java编译器编译好.class文件之后,我们需要使用JVM来运行这个class文件。那么最开始的工作就是要把字节码从磁盘输入到内存中,这个过程我们叫做【加载
】。加载完成之后,我们就可以进行一系列的运行前准备工作了,比如: 为类静态变量开辟空间,将常量池存放在方法区内存中并实现常量池地址解析,初始化类静态变量等等。这篇文章我们要好好谈谈JVM是如何加载class文件的?
1、JVM加载类的过程
当我们使用命令来执行某一个Java程序(比如Test.class)的时候:java Test
(1) java.exe 会帮助我们找到 JRE ,接着找到位于 JRE 内部的 jvm.dll ,这才是真正的 Java 虚拟机器 , 最后加载动态库,激活 Java 虚拟机器。
(2) 虚拟机器激活以后,会先做一些初始化的动作,比如说读取系统参数等。一旦初始化动作完成之后,就会产生第一个类装载器
―― Bootstrap Loader(启动类装载器
(3) Bootstrap Loader 所做的初始工作中,除了一些基本的初始化动作之外,最重要的就是加载 Launcher.java 之中的 ExtClassLoader(扩展类装载器)
,并设定其 Parent 为 null ,代表其父加载器为 BootstrapLoader 。
(4) 然后 Bootstrap Loader 再要求加载 Launcher.java 之中的 AppClassLoader(用户自定义类装载器
) ,并设定其 Parent 为之前产生的 ExtClassLoader 实体。这两个加载器都是以静态类的形式存在的。
这里要请大家注意的是, Launcher$ExtClassLoader.class 与 Launcher$AppClassLoader.class 都是由 Bootstrap Loader 所加载,所以 Parent 和由哪个类加载器加载没有关系。
初学者对这个过程很难理解,我们将在下面详细的讲讲类装载器和"Parent"是什么。
2、类装载器体系结构
JVM加载class文件必须通过一个叫做类装载器的程序,它的作用就是从磁盘文件中将要运行代码的字节码流加载进内存(JVM管理的方法区)中。下面是几个比较重要的概念:
(1) 启动类装载器 :
每个Java虚拟机实现都必须有一个启动类装载器。它只负责在系统类(核心Java API的class文件)的安装路径中查找要装入的类。这个装载器的实现由C++ 所撰写而成,是JVM实现的一部分。
(2) 扩展类装载器和自定义类装载器 :
负责除核心Java API以外的其它class文件的装载。例如、用于安装或下载标准扩展的class文件,在类路径中发现的类库的class文件,用于应用程序运行的class文件等等。这里有一点需要注意:自定义类装载器并非由应用程序员自己实现,它也是JVM
(3) 命名空间:
Java虚拟机为每一个类装载器维护一个唯一标识的命名空间。一个Java程序可以多次装载具有同一个全限定名的多个类。 Java虚拟机要确定这"多个类"的唯一性,因此,当多个类 装载器都装载了同名的类时,为了唯一地标识这个类,还要在类名前加上装载该类的类装载器的标识(指出了类所位于的命名空间)。下图显示了两个类装载器有关的命名空间,显然,不同的类装载器允许装载相同的类Volcano。
命名空间有助于安全的实现,因为你可以有效地在装入了不同命名空间的类之间设置一个防护罩。在Java虚拟机中,在同一个命名空间内的类可以直接进行交 互,而不同的命名空间中的类甚至不能察觉彼此的存在,除非显式地提供了允许它们进行交互的机制。一旦加载后,如果一个恶意的类被赋予权限访问其他虚拟机加 载的当前类,它就可以潜在地知道一些它不应该知道的信息,或者干扰程序的正常运行。
3、双亲委托模型
用户自定义类装载器经常依赖其他类装载器——至少依赖于虚拟机启动时创建的启动类装载器—来帮助它实现一些类装载请求:.在版本1.2前,非启动类装载器 必须显式地求助于其他类装载器,类装载器可以请求另一个用户自定义的类装载器来装载一个类,这个请求是通过对被请求的用户自定义类装载器调用 loadClass()来实现的。除此以外,类装载器也可以通过调用findSystemClass()来请求启动类装载器来装载类,这是类 ClassLoader中的一个静态方法。
在版本1.2中,类装载器请求另一个类装载器来装载类型的过程被形式化,称为双亲委派模式 。
从版本1.2开始、除启动类装载器以外的每一个类装载器,都有一个“双亲”类装载器 ,在某个特定的类装载器试图以常用方式装载类型以前,它会先默认地将这个任务“委派”给它的双亲——清求它的双亲来装载这个类型。这个双亲再依次请求它自 己的双亲来装载这个类型。这个委派的过程一直向上继续,直到达到启动类装载器,通常启动类装载器是委派链中的最后一个类装载器。如果一个类装载器的双亲类 装载器有能力来装载这个类型。则这个类装载器返回这个类型。否则,这个类装载器试图自己来装载这个类。
当Java虚拟机开始运行时,在应用程序开始启动以前,它至少创建一个用户自定义装载器,也可能创建多个.所有这些装载器被连接在一个Parent-Child的委托链中,在这个链的顶端是启动类装载器。
例如:假设你写了一个应用程序,在虚拟机上运行它.虚拟机在启动时实例化了两个用户自定义类装载器:一个"扩展类装载器",一个"类路径类装载器".这些类装载器和启动类装载器一起联入一个Parent-Child委托链中,如下图所示.
上图所示类路径类装载器的Parent是扩展类装载器, 扩展类装载器的Parent是启动类装载器.在图2中,类路径类装载器就被实例为系统类装载器.假设你的程序实例化它的网络类装载器,它就指明了系统类装载器作为它的Parent.
下面的例程说明了类装载器的父子关系.
import java.net.URL;
import java.net.URLClassL
public class ClassLoaderTest {
private static int count = -1;
public static void testClassLoader(Object obj) {
if (count & 0 && obj == null) {
System.out.println("Input object is NULL";
ClassLoader cl =
if (obj != null && !(obj instanceof ClassLoader)) {
cl = obj.getClass().getClassLoader();
} else if (obj != null) {
cl = (ClassLoader)
String parent = "";
for (int i = 0; i & i++) {
parent += "Parent ";
if (cl != null) {
System.out.println(
parent + "ClassLoader name = " + cl.getClass().getName());
testClassLoader(cl.getParent());
System.out.println(
parent + "ClassLoader name = BootstrapClassLoader";
count = -1;
public static void main(String[] args) {
URL[] urls = new URL[1];
URLClassLoader urlLoader = new URLClassLoader(urls);
ClassLoaderTest.testClassLoader(urlLoader);
以上例程的输出为:
ClassLoader name = java.net.URLClassLoader
Parent ClassLoader name = sun.misc.Launcher$AppClassLoader
Parent Parent ClassLoader name = sun.misc.Launcher$ExtClassLoader
Parent Parent Parent ClassLoader name = BootstrapClassLoader
类装载器请求过程
以上例程1为例.将main方法改为:
ClassLoaderTest tc = new ClassLoaderTest();
ClassLoaderTest.testClassLoader(tc);
ClassLoader name = sun.misc.Launcher$AppClassLoader
Parent ClassLoader name = sun.misc.Launcher$ExtClassLoader
Parent Parent ClassLoader name = BootstrapClassLoader
程序运行过程中,类路径类装载器发出一个装载ClassLoaderTest类的请求, 类路径类装载器必须首先询问它的Parent---扩展类装载器 ---来查找并装载这个类,同样扩展类装载器首先询问启动类装载器。由于ClassLoaderTest不是 Java API(JAVA_HOME\jre\lib)中的类,也不在已安装扩展路径(JAVA_HOME\jre\lib\ext)上,这两类装载器 都将返回而不会提供一个名为ClassLoaderTest的已装载类给类路径类装载器。类路径类装载器只能以它自己的方式来装载 ClassLoaderTest,它会从当前类路径上下载这个类。这样,ClassLoaderTest就可以在应用程序后面的执行中发挥作用。
在上例中,ClassLoaderTest类的testClassLoader方法被首次调用,该方法引用了Java API中的类 java.lang.String。Java虚拟机会请求装载ClassLoaderTest类的类路径类装载器来装载 java.lang.String。就像前面一样,类路径类装载器首先将请求传递给它的Parent类装载器,然后这个请求一路被委托到启动类装载器。但 是,启动类装载器可以将java.lang.String类返回给类路径类装载器,因为它可以找到这个类,这样扩展类装载器就不必在已安装扩展路径中查找 这个类,类路径类装载器也不必在类路径中查找这个类。扩展类装载器和类路径类装载器仅需要返回由启动类装载器返回的类java.lang.String。 从这一刻开始,不管何时ClassLoaderTest类引用了名为java.lang.String的类,虚拟机就可以直接使用这个 java.lang.String类了。
4、一个经典的实例说明
我们看看下面的代码:
package java.
public class String {
public static void main(String[] args){
大家发现什么不同了吗?对了,我们写了一个与JDK中String一模一样的类,连包java.lang都一样,唯一不同的是我们自定义的String类有一个main函数。我们来运行一下:
java.lang.NoSuchMethodError: main
Exception in thread "main"
这是为什么? 我们的String类不是明明有main方法吗?
其实联系我们上面讲到的双亲委托模型,我们就能解释这个问题了。
运行这段代码,JVM会首先创建一个自定义类加载器,不妨叫做AppClassLoader,并把这个加载器链接到委托链中:AppClassLoader -& ExtClassLoader -& BootstrapLoader。
然后AppClassLoader会将加载java.lang.String的请求委托给ExtClassLoader,而 ExtClassLoader又会委托给最后的启动类加载器BootstrapLoader。
启动类加载器BootstrapLoader只能加载JAVA_HOME\jre\lib中的class类(即J2SE API),问题是标准API中确实有一个java.lang.String(注意,这个类和我们自定义的类是完全两个类)。BootstrapLoader以为找到了这个类,毫不犹豫的加载了j2se api中的java.lang.String。
最后出现上面的加载错误(注意不是异常,是错误,JVM退出),因为API中的String类是没有main方法的。
结论:我们当然可以自定义一个和API完全一样的类,但是由于双亲委托模型,使得我们不可能加载上我们自定义的这样一个类。所以J2SE规范中希望我们自定义的包有自己唯一的特色(网络域名)。还有一点,这种加载器原理使得JVM更加安全的运行程序,因为黑客很难随意的替代掉API中的代码了。
浏览 31295
Heart.X.Raid
浏览: 603595 次
来自: 武汉
有点明白了
xiaoqishup 写道字节和位的概念根本就没有分清。boo ...
请问可以转载吗?
可能随着网络资源越来越容易获取,后来很多人都不假思索地摘抄别人 ...运行java的class文件方法详解
投稿:hebedich
字体:[ ] 类型:转载 时间:
这篇文章主要详细介绍了运行java的class文件方法的相关资料,需要的朋友可以参考下
一、运行class文件
执行带main方法的class文件,命令行为:
java &CLASS文件名&
注意:CLASS文件名不要带文件后缀.class
如果执行的class文件是带包的,即在类文件中使用了:package &包名&
那应该在包的基路径下执行,命令行为:
java &包名&.CLASS文件名
例如:PackageTest.java中,其包名为:com.ee2ee.test,对应的语句为:
package com.ee2ee.
PackageTest.java及编译后的class文件PackageTest.class的存放目录如下:
&&&&& |__ee2ee
&&&&&&&&&& |__test
&&&&&&&&&&&&&&& |__PackageTest.java
&&&&&&&&&&&&&&& |__PackageTest.class
要运行PackageTest.class,应在classes目录下执行:
java com.ee2ee.test.PackageTest
二、运行jar文件中的class
&&&&&&& 原理和运行class文件一样,只需加上参数-cp &jar文件名&即可。
&&&&&&& 例如:执行test.jar中的类com.ee2ee.test.PackageTest,命令行如下:
java -cp test.jar com.ee2ee.test.PackageTest
三、显示jdk版本信息
&&&&&&& 当一台机器上有多个jdk版本时,需要知道当前使用的是那个版本的jdk,使用参数-version即可知道其版本,命令行为:
java -version
四、增加虚拟机可以使用的最大内存
&&&&&&& java虚拟机可使用的最大内存是有限制的,缺省值通常为64MB或128MB。如果一个应用程序为了提高性能而把数据加载内存中而占用较大的内存,比如超过了默认的最大值128MB,需要加大java虚拟机可使用的最大内存,否则会出现Out of Memory(系统内存不足)的异常。启动java时,需要使用如下两个参数:
&&&&&&& -Xms java虚拟机初始化时使用的内存大小
&&&&&&& -Xmx java虚拟机可以使用的最大内存
&&&&&&& 以上两个参数中设置的size,可以带单位,例如:256m表示256MB
举例说明:
java -Xms128m -Xmx256m ...
表示java虚拟机初始化时使用的内存为128MB,可使用的最大内存为256MB。
对于tomcat,可以修改其脚本catalina.sh(unix平台)或catalina.bat(windows平台),设置变量JAVA_OPTS即可,例如:
JAVA_OPTS='-Xms128m -Xmx256m'
在控制台输出信息中,有个-X(注意是大写)的命令,这个正是查看JVM配置参数的命令。
其次,用java -X 命令查看JVM的配置说明:
1、-Xmixed mixed mode execution (default)
&混合模式执行
2、-Xint interpreted mode execution only
&解释模式执行
3、-Xbootclasspath:&directories and zip/jar&
&&&&& set search path for bootstrap classes and resources
&设置zip/jar资源或者类(.class文件)存放目录路径
3、-Xbootclasspath/a:&directories and zip/jar&
&&&&& append to end of bootstrap class path
&追加zip/jar资源或者类(.class文件)存放目录路径
4、-Xbootclasspath/p:&directories and zip/jar&
&&&&& prepend in front of bootstrap class path
&预先加载zip/jar资源或者类(.class文件)存放目录路径
5、-Xnoclassgc disable class garbage collection
&关闭类垃圾回收功能
6、-Xincgc enable incremental garbage collection
&开启类的垃圾回收功能
7、-Xloggc:&file& log GC status to a file with time stamps
&记录垃圾回日志到一个文件。
8、-Xbatch disable background compilation
&关闭后台编译
9、-Xms&size& set initial Java heap size
&设置JVM初始化堆内存大小
10、-Xmx&size& set maximum Java heap size
&设置JVM最大的堆内存大小
11、-Xss&size& set java thread stack size
&设置JVM栈内存大小
12、-Xprof output cpu profiling data
&输入CPU概要表数据
13、-Xfuture enable strictest checks, anticipating future default
&执行严格的代码检查,预测可能出现的情况
14、-Xrs reduce use of OS signals by Java/VM (see documentation)
&通过JVM还原操作系统信号
15、-Xcheck:jni perform. additional checks for JNI functions
&对JNI函数执行检查
16、-Xshare:off do not attempt to use shared class data
&尽可能不去使用共享类的数据
17、-Xshare:auto use shared class data if possible (default)
&尽可能的使用共享类的数据
18、-Xshare:on require using shared class data, otherwise fail.
&尽可能的使用共享类的数据,否则运行失败
怎么用这这些参数呢?其实所有的命令行都是这么一用,下面我就给出一个最简单的HelloWorl的例子来演示这个参数的用法,非常的简单。
// HelloWorld.java
public class HelloWorld {
&&& public static void main(String[] args){
&&&&&&& System.out.println("Hello World!");
编译并运行:
D:\j2sdk15\bin&javac HelloWorld.java
D:\j2sdk15\bin&java -Xms256M -Xmx512M HelloWorld
Hello World!
以上所述就是本文的全部内容了,希望大家对运行java的class文件的方法有了新的认识。
您可能感兴趣的文章:
大家感兴趣的内容
12345678910
最近更新的内容
常用在线小工具

我要回帖

更多关于 mac怎么打开class文件 的文章

 

随机推荐