用自己的话总结一下jvm的工作原理:
1.java对象都是以class的对象存在的,所以在编译的时候可以看到,java文件中有内部类的时候,会编译成javafilename$innerClassName.class的这种形式
2.jvm加载class的时候有一个顺序,jvm是通过classpath加载class的,class path 分为三层,bootstrap,extension,application
---------------------------转自:http://7-sun.com/text/5501.html
Bootstrap Class Loader ,Extension Class Loader ,Application Class Loader三种Class Loader是JVM 系统已经事先实现。
Bootstrap Class Loader 采用的是C或其他相应语言编写(根据JRE 操作系统版本不同而不同),其他两种Class Loader 均采用Java 语言编写,他们的实现为ExtClassLoader 与AppClassLoader 两个内部静态类,位于sun.misc.Launcher内,而Launcher则位于rt.jar中。
以下网址可以下载JDK 的源码实现:
http://download.java.net/openjdk/jdk6/
通过源码可以是我们更进一步了解Class Loader 的内部实现及流程。
Bootstrap Class Loader
Bootstrap Class Loader 的实现位于${openjdk}\hotspot\src\share\vm\classfile 目录下的 classLoader.cpp 与classLoader.hpp,打开源代码可以发现Bootstrap Class Loader是采用C++ 编写的,这就是上一节代码中为什么得不到Bootstrap Class Loader 对象的原因。
该ClassLoader 可以做到根据类文件名来加载类,这其实就和java.lang.ClassLoader 的loadClass 功能非常相近了。
ClassFileStream* ClassPathDirEntry::open_stream(const char* name) {
...
jio_snprintf(path, sizeof(path), "%s%s%s", _dir,
os::file_separator(), name)
...
}
可以看到path=_dir + 文件分隔符 + name,得到path 后,就是读取文件并存放到byte[] 的过程了。
要想path 直接可用,得有两个前提:
(1) _dir事先已经配置好;
(2)name 也应该先经过了处理;
例如:
java.lang.String,会先被转换成java/lang/String.class;
又假如:
_dir 为/usr/local/java/src;
name 为com/test/HelloWorld.class;
path 最终即为/usr/local/java/src/com/test/HelloWorld.class;
加载类文件过程:
instanceKlassHandle ClassLoader::load_classfile(symbolHandle h_name, TRAPS) {
ResourceMark rm(THREAD);
EventMark m("loading class " INTPTR_FORMAT, (address)h_name());
ThreadProfilerMark tpm(ThreadProfilerMark::classLoaderRegion);
stringStream st;
// st.print() uses too much stack space while handling a StackOverflowError
// st.print("%s.class", h_name->as_utf8());
st.print_raw(h_name->as_utf8());
st.print_raw(".class");
char* name = st.as_string();
// Lookup stream for parsing .class file
ClassFileStream* stream = NULL;
int classpath_index = 0;
{
PerfClassTraceTime vmtimer(perf_sys_class_lookup_time(),
((JavaThread*) THREAD)->get_thread_stat()->perf_timers_addr(),
PerfClassTraceTime::CLASS_LOAD);
ClassPathEntry* e = _first_entry;
while (e != NULL) {
stream = e->open_stream(name);
if (stream != NULL) {
break;
}
e = e->next();
++classpath_index;
}
}
instanceKlassHandle h(THREAD, klassOop(NULL));
if (stream != NULL) {
// class file found, parse it
ClassFileParser parser(stream);
Handle class_loader;
Handle protection_domain;
symbolHandle parsed_name;
instanceKlassHandle result = parser.parseClassFile(h_name,
class_loader,
protection_domain,
parsed_name,
false,
CHECK_(h));
// add to package table
if (add_package(name, classpath_index, THREAD)) {
h = result;
}
}
return h;
}
初始化过程:
void ClassLoader::initialize() {
...
// lookup zip library entry points
load_zip_library();
// initialize search path
setup_bootstrap_search_path();
...
}
其中load_zip_library() 即加载zip 类库,从而便于处理jar (可以认为jar 是一种特殊的zip 压缩);而setup_bootstrap_search_path() 即为初始化BootstrapClassLoader 的查找路径,其实就是在初始化上文中每个ClassPathEntry 的_dir。
打开${openjdk}\hotspot\src\share\vm\runtime\os.cpp 文件,找到set_boot_path()方法,会发现Bootstrap Class Loader 已经将所要加载的类库jar 文件固化至代码中了,这些就是JVM 启动时必须要加载的文件。
bool os::set_boot_path(char fileSep, char pathSep) {
const char* home = Arguments::get_java_home();
int home_len = (int)strlen(home);
static const char* meta_index_dir_format = "%/lib/";
static const char* meta_index_format = "%/lib/meta-index";
char* meta_index = format_boot_path(meta_index_format, home, home_len, fileSep, pathSep);
if (meta_index == NULL) return false;
char* meta_index_dir = format_boot_path(meta_index_dir_format, home, home_len, fileSep, pathSep);
if (meta_index_dir == NULL) return false;
Arguments::set_meta_index_path(meta_index, meta_index_dir);
// Any modification to the JAR-file list, for the boot classpath must be
// aligned with install/install/make/common/Pack.gmk. Note: boot class
// path class JARs, are stripped for StackMapTable to reduce download size.
static const char classpath_format[] =
"%/lib/resources.jar:"
"%/lib/rt.jar:"
"%/lib/sunrsasign.jar:"
"%/lib/jsse.jar:"
"%/lib/jce.jar:"
"%/lib/charsets.jar:"
// ## TEMPORARY hack to keep the legacy launcher working when
// ## only the boot module is installed (cf. j.l.ClassLoader)
"%/lib/modules/jdk.boot.jar:"
"%/classes";
char* sysclasspath = format_boot_path(classpath_format, home, home_len, fileSep, pathSep);
if (sysclasspath == NULL) return false;
Arguments::set_sysclasspath(sysclasspath);
return true;
}
本人对C++ 不甚了解,所以无法就代码及流程做更进一步的讲解,希望大家谅解,也欢迎大家补充。
AppClassLoader 与ExtClassLoader
打开sun.misc.Launcher 的源代码,源代码位置在${openjdk}\jdk\src\share\classes\sun\misc\Launcher.java ,ExtClassLoader 与AppClassLoader 是Launcher 类的内部类,代码如下:
/*
* The class loader used for loading installed extensions.
*/
static class ExtClassLoader extends URLClassLoader {}
/**
* The class loader used for loading from java.class.path.
* runs in a restricted security context.
*/
static class AppClassLoader extends URLClassLoader {}
从继承关系可以向上一直追溯直至java.lang.ClassLoader 实现类,两者的继承关系如下:
java.lang.Object
--- java.lang.ClassLoader
--- java.security.SecureClassLoader
--- java.net.URLClassLoader
--- sun.misc.Launcher$ExtClassLoader
java.lang.Object
--- java.lang.ClassLoader
--- java.security.SecureClassLoader
--- java.net.URLClassLoader
--- sun.misc.Launcher$AppClassLoader
从下图可以更直观的观察出他们的继承关系(引用自网络):
AppClassLoader 与ExtClassLoader 基本都是调用父类的方法,只是在其中加入了一些相应的业务逻辑,所以这里就不分析他们了,在ClassLoader这节中会更详细的分析。