src/share/vm/classfile/classLoader.cpp
Print this page
rev 6853 : 8046070: Class Data Sharing clean up and refactoring
Summary: Cleaned up CDS to be more configurable, maintainable and extensible
Reviewed-by: dholmes, coleenp, acorn, mchung
@@ -24,19 +24,25 @@
#include "precompiled.hpp"
#include "classfile/classFileParser.hpp"
#include "classfile/classFileStream.hpp"
#include "classfile/classLoader.hpp"
+#include "classfile/classLoaderExt.hpp"
#include "classfile/classLoaderData.inline.hpp"
#include "classfile/javaClasses.hpp"
+#if INCLUDE_CDS
+#include "classfile/sharedPathsMiscInfo.hpp"
+#include "classfile/sharedClassUtil.hpp"
+#endif
#include "classfile/systemDictionary.hpp"
#include "classfile/vmSymbols.hpp"
#include "compiler/compileBroker.hpp"
#include "gc_interface/collectedHeap.inline.hpp"
#include "interpreter/bytecodeStream.hpp"
#include "interpreter/oopMapCache.hpp"
#include "memory/allocation.inline.hpp"
+#include "memory/filemap.hpp"
#include "memory/generation.hpp"
#include "memory/oopFactory.hpp"
#include "memory/universe.inline.hpp"
#include "oops/instanceKlass.hpp"
#include "oops/instanceRefKlass.hpp"
@@ -112,12 +118,16 @@
PerfCounter* ClassLoader::_isUnsyncloadClass = NULL;
PerfCounter* ClassLoader::_load_instance_class_failCounter = NULL;
ClassPathEntry* ClassLoader::_first_entry = NULL;
ClassPathEntry* ClassLoader::_last_entry = NULL;
+int ClassLoader::_num_entries = 0;
PackageHashtable* ClassLoader::_package_hash_table = NULL;
+#if INCLUDE_CDS
+SharedPathsMiscInfo* ClassLoader::_shared_paths_misc_info = NULL;
+#endif
// helper routines
bool string_starts_with(const char* str, const char* str_to_find) {
size_t str_len = strlen(str);
size_t str_to_find_len = strlen(str_to_find);
if (str_to_find_len > str_len) {
@@ -192,10 +202,19 @@
return NULL;
}
// check if file exists
struct stat st;
if (os::stat(path, &st) == 0) {
+#if INCLUDE_CDS
+ if (DumpSharedSpaces) {
+ // We have already check in ClassLoader::check_shared_classpath() that the directory is empty, so
+ // we should never find a file underneath it -- unless user has added a new file while we are running
+ // the dump, in which case let's quit!
+ ShouldNotReachHere();
+ ClassLoader::exit_with_path_failure("VM internal error. Classes can be loaded only from JAR files during dump time: %s", path);
+ }
+#endif
// found file, open it
int file_handle = os::open(path, 0, 0);
if (file_handle != -1) {
// read contents into resource array
u1* buffer = NEW_RESOURCE_ARRAY(u1, st.st_size);
@@ -226,39 +245,53 @@
(*ZipClose)(_zip);
}
FREE_C_HEAP_ARRAY(char, _zip_name, mtClass);
}
-ClassFileStream* ClassPathZipEntry::open_stream(const char* name, TRAPS) {
+u1* ClassPathZipEntry::open_entry(const char* name, jint* filesize, bool nul_terminate, TRAPS) {
// enable call to C land
JavaThread* thread = JavaThread::current();
ThreadToNativeFromVM ttn(thread);
// check whether zip archive contains name
- jint filesize, name_len;
- jzentry* entry = (*FindEntry)(_zip, name, &filesize, &name_len);
+ jint name_len;
+ jzentry* entry = (*FindEntry)(_zip, name, filesize, &name_len);
if (entry == NULL) return NULL;
u1* buffer;
char name_buf[128];
char* filename;
if (name_len < 128) {
filename = name_buf;
} else {
filename = NEW_RESOURCE_ARRAY(char, name_len + 1);
}
- // file found, get pointer to class in mmaped jar file.
+ // file found, get pointer to the entry in mmapped jar file.
if (ReadMappedEntry == NULL ||
!(*ReadMappedEntry)(_zip, entry, &buffer, filename)) {
- // mmaped access not available, perhaps due to compression,
+ // mmapped access not available, perhaps due to compression,
// read contents into resource array
- buffer = NEW_RESOURCE_ARRAY(u1, filesize);
+ int size = (*filesize) + ((nul_terminate) ? 1 : 0);
+ buffer = NEW_RESOURCE_ARRAY(u1, size);
if (!(*ReadEntry)(_zip, entry, buffer, filename)) return NULL;
}
+
+ // return result
+ if (nul_terminate) {
+ buffer[*filesize] = 0;
+ }
+ return buffer;
+}
+
+ClassFileStream* ClassPathZipEntry::open_stream(const char* name, TRAPS) {
+ jint filesize;
+ u1* buffer = open_entry(name, &filesize, false, CHECK_NULL);
+ if (buffer == NULL) {
+ return NULL;
+ }
if (UsePerfData) {
ClassLoader::perf_sys_classfile_bytes_read()->inc(filesize);
}
- // return result
return new ClassFileStream(buffer, filesize, _zip_name); // Resource allocated
}
// invoke function for each entry in the zip file
void ClassPathZipEntry::contents_do(void f(const char* name, void* context), void* context) {
@@ -270,16 +303,17 @@
if (ze == NULL) break;
(*f)(ze->name, context);
}
}
-LazyClassPathEntry::LazyClassPathEntry(char* path, const struct stat* st) : ClassPathEntry() {
+LazyClassPathEntry::LazyClassPathEntry(char* path, const struct stat* st, bool throw_exception) : ClassPathEntry() {
_path = os::strdup_check_oom(path);
_st = *st;
_meta_index = NULL;
_resolved_entry = NULL;
_has_error = false;
+ _throw_exception = throw_exception;
}
LazyClassPathEntry::~LazyClassPathEntry() {
os::free(_path);
}
@@ -291,11 +325,15 @@
ClassPathEntry* LazyClassPathEntry::resolve_entry(TRAPS) {
if (_resolved_entry != NULL) {
return (ClassPathEntry*) _resolved_entry;
}
ClassPathEntry* new_entry = NULL;
- new_entry = ClassLoader::create_class_path_entry(_path, &_st, false, CHECK_NULL);
+ new_entry = ClassLoader::create_class_path_entry(_path, &_st, false, _throw_exception, CHECK_NULL);
+ if (!_throw_exception && new_entry == NULL) {
+ assert(!HAS_PENDING_EXCEPTION, "must be");
+ return NULL;
+ }
{
ThreadCritical tc;
if (_resolved_entry == NULL) {
_resolved_entry = new_entry;
return new_entry;
@@ -325,29 +363,93 @@
bool LazyClassPathEntry::is_lazy() {
return true;
}
+u1* LazyClassPathEntry::open_entry(const char* name, jint* filesize, bool nul_terminate, TRAPS) {
+ if (_has_error) {
+ return NULL;
+ }
+ ClassPathEntry* cpe = resolve_entry(THREAD);
+ if (cpe == NULL) {
+ _has_error = true;
+ return NULL;
+ } else if (cpe->is_jar_file()) {
+ return ((ClassPathZipEntry*)cpe)->open_entry(name, filesize, nul_terminate,THREAD);
+ } else {
+ ShouldNotReachHere();
+ *filesize = 0;
+ return NULL;
+ }
+}
+
static void print_meta_index(LazyClassPathEntry* entry,
GrowableArray<char*>& meta_packages) {
tty->print("[Meta index for %s=", entry->name());
for (int i = 0; i < meta_packages.length(); i++) {
if (i > 0) tty->print(" ");
tty->print("%s", meta_packages.at(i));
}
tty->print_cr("]");
}
+#if INCLUDE_CDS
+void ClassLoader::exit_with_path_failure(const char* error, const char* message) {
+ assert(DumpSharedSpaces, "only called at dump time");
+ tty->print_cr("Hint: enable -XX:+TraceClassPaths to diagnose the failure");
+ vm_exit_during_initialization(error, message);
+}
+#endif
+
+void ClassLoader::trace_class_path(const char* msg, const char* name) {
+ if (!TraceClassPaths) {
+ return;
+ }
-void ClassLoader::setup_meta_index() {
+ if (msg) {
+ tty->print("%s", msg);
+ }
+ if (name) {
+ if (strlen(name) < 256) {
+ tty->print("%s", name);
+ } else {
+ // For very long paths, we need to print each character separately,
+ // as print_cr() has a length limit
+ while (name[0] != '\0') {
+ tty->print("%c", name[0]);
+ name++;
+ }
+ }
+ }
+ if (msg && msg[0] == '[') {
+ tty->print_cr("]");
+ } else {
+ tty->cr();
+ }
+}
+
+void ClassLoader::setup_bootstrap_meta_index() {
// Set up meta index which allows us to open boot jars lazily if
// class data sharing is enabled
+ const char* meta_index_path = Arguments::get_meta_index_path();
+ const char* meta_index_dir = Arguments::get_meta_index_dir();
+ setup_meta_index(meta_index_path, meta_index_dir, 0);
+}
+
+void ClassLoader::setup_meta_index(const char* meta_index_path, const char* meta_index_dir, int start_index) {
const char* known_version = "% VERSION 2";
- char* meta_index_path = Arguments::get_meta_index_path();
- char* meta_index_dir = Arguments::get_meta_index_dir();
FILE* file = fopen(meta_index_path, "r");
int line_no = 0;
+#if INCLUDE_CDS
+ if (DumpSharedSpaces) {
+ if (file != NULL) {
+ _shared_paths_misc_info->add_required_file(meta_index_path);
+ } else {
+ _shared_paths_misc_info->add_nonexist_path(meta_index_path);
+ }
+ }
+#endif
if (file != NULL) {
ResourceMark rm;
LazyClassPathEntry* cur_entry = NULL;
GrowableArray<char*> boot_class_path_packages(10);
char package_name[256];
@@ -378,23 +480,25 @@
case '@':
{
// Hand off current packages to current lazy entry (if any)
if ((cur_entry != NULL) &&
(boot_class_path_packages.length() > 0)) {
- if (TraceClassLoading && Verbose) {
+ if ((TraceClassLoading || TraceClassPaths) && Verbose) {
print_meta_index(cur_entry, boot_class_path_packages);
}
MetaIndex* index = new MetaIndex(boot_class_path_packages.adr_at(0),
boot_class_path_packages.length());
cur_entry->set_meta_index(index);
}
cur_entry = NULL;
boot_class_path_packages.clear();
// Find lazy entry corresponding to this jar file
- for (ClassPathEntry* entry = _first_entry; entry != NULL; entry = entry->next()) {
- if (entry->is_lazy() &&
+ int count = 0;
+ for (ClassPathEntry* entry = _first_entry; entry != NULL; entry = entry->next(), count++) {
+ if (count >= start_index &&
+ entry->is_lazy() &&
string_starts_with(entry->name(), meta_index_dir) &&
string_ends_with(entry->name(), &package_name[2])) {
cur_entry = (LazyClassPathEntry*) entry;
break;
}
@@ -427,61 +531,116 @@
}
}
// Hand off current packages to current lazy entry (if any)
if ((cur_entry != NULL) &&
(boot_class_path_packages.length() > 0)) {
- if (TraceClassLoading && Verbose) {
+ if ((TraceClassLoading || TraceClassPaths) && Verbose) {
print_meta_index(cur_entry, boot_class_path_packages);
}
MetaIndex* index = new MetaIndex(boot_class_path_packages.adr_at(0),
boot_class_path_packages.length());
cur_entry->set_meta_index(index);
}
fclose(file);
}
}
+#if INCLUDE_CDS
+void ClassLoader::check_shared_classpath(const char *path) {
+ if (strcmp(path, "") == 0) {
+ exit_with_path_failure("Cannot have empty path in archived classpaths", NULL);
+ }
+
+ struct stat st;
+ if (os::stat(path, &st) == 0) {
+ if ((st.st_mode & S_IFREG) != S_IFREG) { // is directory
+ if (!os::dir_is_empty(path)) {
+ tty->print_cr("Error: non-empty directory '%s'", path);
+ exit_with_path_failure("CDS allows only empty directories in archived classpaths", NULL);
+ }
+ }
+ }
+}
+#endif
+
void ClassLoader::setup_bootstrap_search_path() {
assert(_first_entry == NULL, "should not setup bootstrap class search path twice");
char* sys_class_path = os::strdup_check_oom(Arguments::get_sysclasspath());
- if (TraceClassLoading && Verbose) {
- tty->print_cr("[Bootstrap loader class path=%s]", sys_class_path);
+ if (!PrintSharedArchiveAndExit) {
+ trace_class_path("[Bootstrap loader class path=", sys_class_path);
}
+#if INCLUDE_CDS
+ if (DumpSharedSpaces) {
+ _shared_paths_misc_info->add_boot_classpath(Arguments::get_sysclasspath());
+ }
+#endif
+ setup_search_path(sys_class_path);
+ os::free(sys_class_path);
+}
+
+#if INCLUDE_CDS
+int ClassLoader::get_shared_paths_misc_info_size() {
+ return _shared_paths_misc_info->get_used_bytes();
+}
+
+void* ClassLoader::get_shared_paths_misc_info() {
+ return _shared_paths_misc_info->buffer();
+}
- int len = (int)strlen(sys_class_path);
+bool ClassLoader::check_shared_paths_misc_info(void *buf, int size) {
+ SharedPathsMiscInfo* checker = SharedClassUtil::allocate_shared_paths_misc_info((char*)buf, size);
+ bool result = checker->check();
+ delete checker;
+ return result;
+}
+#endif
+
+void ClassLoader::setup_search_path(char *class_path) {
+ int offset = 0;
+ int len = (int)strlen(class_path);
int end = 0;
// Iterate over class path entries
for (int start = 0; start < len; start = end) {
- while (sys_class_path[end] && sys_class_path[end] != os::path_separator()[0]) {
+ while (class_path[end] && class_path[end] != os::path_separator()[0]) {
end++;
}
- char* path = NEW_C_HEAP_ARRAY(char, end-start+1, mtClass);
- strncpy(path, &sys_class_path[start], end-start);
- path[end-start] = '\0';
+ EXCEPTION_MARK;
+ ResourceMark rm(THREAD);
+ char* path = NEW_RESOURCE_ARRAY(char, end - start + 1);
+ strncpy(path, &class_path[start], end - start);
+ path[end - start] = '\0';
update_class_path_entry_list(path, false);
- FREE_C_HEAP_ARRAY(char, path, mtClass);
- while (sys_class_path[end] == os::path_separator()[0]) {
+#if INCLUDE_CDS
+ if (DumpSharedSpaces) {
+ check_shared_classpath(path);
+ }
+#endif
+ while (class_path[end] == os::path_separator()[0]) {
end++;
}
}
- os::free(sys_class_path);
}
-ClassPathEntry* ClassLoader::create_class_path_entry(char *path, const struct stat* st, bool lazy, TRAPS) {
+ClassPathEntry* ClassLoader::create_class_path_entry(char *path, const struct stat* st,
+ bool lazy, bool throw_exception, TRAPS) {
JavaThread* thread = JavaThread::current();
if (lazy) {
- return new LazyClassPathEntry(path, st);
+ return new LazyClassPathEntry(path, st, throw_exception);
}
ClassPathEntry* new_entry = NULL;
if ((st->st_mode & S_IFREG) == S_IFREG) {
// Regular file, should be a zip file
// Canonicalized filename
char canonical_path[JVM_MAXPATHLEN];
if (!get_canonical_path(path, canonical_path, JVM_MAXPATHLEN)) {
// This matches the classic VM
+ if (throw_exception) {
THROW_MSG_(vmSymbols::java_io_IOException(), "Bad pathname", NULL);
+ } else {
+ return NULL;
+ }
}
char* error_msg = NULL;
jzfile* zip;
{
// enable call to C land
@@ -489,11 +648,11 @@
HandleMark hm(thread);
zip = (*ZipOpen)(canonical_path, &error_msg);
}
if (zip != NULL && error_msg == NULL) {
new_entry = new ClassPathZipEntry(zip, path);
- if (TraceClassLoading) {
+ if (TraceClassLoading || TraceClassPaths) {
tty->print_cr("[Opened %s]", path);
}
} else {
ResourceMark rm(thread);
char *msg;
@@ -503,16 +662,20 @@
} else {
int len = (int)(strlen(path) + strlen(error_msg) + 128);
msg = NEW_RESOURCE_ARRAY(char, len); ;
jio_snprintf(msg, len - 1, "error in opening JAR file <%s> %s", error_msg, path);
}
+ if (throw_exception) {
THROW_MSG_(vmSymbols::java_lang_ClassNotFoundException(), msg, NULL);
+ } else {
+ return NULL;
+ }
}
} else {
// Directory
new_entry = new ClassPathDirEntry(path);
- if (TraceClassLoading) {
+ if (TraceClassLoading || TraceClassPaths) {
tty->print_cr("[Path %s]", path);
}
}
return new_entry;
}
@@ -569,27 +732,41 @@
} else {
_last_entry->set_next(new_entry);
_last_entry = new_entry;
}
}
+ _num_entries ++;
}
-void ClassLoader::update_class_path_entry_list(char *path,
- bool check_for_duplicates) {
+// Returns true IFF the file/dir exists and the entry was successfully created.
+bool ClassLoader::update_class_path_entry_list(char *path,
+ bool check_for_duplicates,
+ bool throw_exception) {
struct stat st;
if (os::stat(path, &st) == 0) {
// File or directory found
ClassPathEntry* new_entry = NULL;
Thread* THREAD = Thread::current();
- new_entry = create_class_path_entry(path, &st, LazyBootClassLoader, CHECK);
+ new_entry = create_class_path_entry(path, &st, LazyBootClassLoader, throw_exception, CHECK_(false));
+ if (new_entry == NULL) {
+ return false;
+ }
// The kernel VM adds dynamically to the end of the classloader path and
// doesn't reorder the bootclasspath which would break java.lang.Package
// (see PackageInfo).
// Add new entry to linked list
if (!check_for_duplicates || !contains_entry(new_entry)) {
- add_to_list(new_entry);
+ ClassLoaderExt::add_class_path_entry(path, check_for_duplicates, new_entry);
}
+ return true;
+ } else {
+#if INCLUDE_CDS
+ if (DumpSharedSpaces) {
+ _shared_paths_misc_info->add_nonexist_path(path);
+ }
+ return false;
+#endif
}
}
void ClassLoader::print_bootclasspath() {
ClassPathEntry* e = _first_entry;
@@ -737,59 +914,56 @@
}
}
assert(n == number_of_entries(), "just checking");
}
- void copy_table(char** top, char* end, PackageHashtable* table);
+ CDS_ONLY(void copy_table(char** top, char* end, PackageHashtable* table);)
};
-
+#if INCLUDE_CDS
void PackageHashtable::copy_table(char** top, char* end,
PackageHashtable* table) {
// Copy (relocate) the table to the shared space.
BasicHashtable<mtClass>::copy_table(top, end);
// Calculate the space needed for the package name strings.
int i;
- int n = 0;
- for (i = 0; i < table_size(); ++i) {
- for (PackageInfo* pp = table->bucket(i);
- pp != NULL;
- pp = pp->next()) {
- n += (int)(strlen(pp->pkgname()) + 1);
- }
- }
- if (*top + n + sizeof(intptr_t) >= end) {
- report_out_of_shared_space(SharedMiscData);
- }
-
- // Copy the table data (the strings) to the shared space.
- n = align_size_up(n, sizeof(HeapWord));
- *(intptr_t*)(*top) = n;
- *top += sizeof(intptr_t);
+ intptr_t* tableSize = (intptr_t*)(*top);
+ *top += sizeof(intptr_t); // For table size
+ char* tableStart = *top;
for (i = 0; i < table_size(); ++i) {
for (PackageInfo* pp = table->bucket(i);
pp != NULL;
pp = pp->next()) {
int n1 = (int)(strlen(pp->pkgname()) + 1);
+ if (*top + n1 >= end) {
+ report_out_of_shared_space(SharedMiscData);
+ }
pp->set_pkgname((char*)memcpy(*top, pp->pkgname(), n1));
*top += n1;
}
}
*top = (char*)align_size_up((intptr_t)*top, sizeof(HeapWord));
+ if (*top >= end) {
+ report_out_of_shared_space(SharedMiscData);
+ }
+
+ // Write table size
+ intptr_t len = *top - (char*)tableStart;
+ *tableSize = len;
}
void ClassLoader::copy_package_info_buckets(char** top, char* end) {
_package_hash_table->copy_buckets(top, end);
}
void ClassLoader::copy_package_info_table(char** top, char* end) {
_package_hash_table->copy_table(top, end, _package_hash_table);
}
-
+#endif
PackageInfo* ClassLoader::lookup_package(const char *pkgname) {
const char *cp = strrchr(pkgname, '/');
if (cp != NULL) {
// Package prefix found
@@ -878,56 +1052,68 @@
}
instanceKlassHandle ClassLoader::load_classfile(Symbol* h_name, TRAPS) {
ResourceMark rm(THREAD);
- EventMark m("loading class %s", h_name->as_C_string());
+ const char* class_name = h_name->as_C_string();
+ EventMark m("loading class %s", class_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();
+ const char* file_name = st.as_string();
+ ClassLoaderExt::Context context(class_name, file_name, THREAD);
// Lookup stream for parsing .class file
ClassFileStream* stream = NULL;
int classpath_index = 0;
+ ClassPathEntry* e = NULL;
+ instanceKlassHandle h;
{
PerfClassTraceTime vmtimer(perf_sys_class_lookup_time(),
((JavaThread*) THREAD)->get_thread_stat()->perf_timers_addr(),
PerfClassTraceTime::CLASS_LOAD);
- ClassPathEntry* e = _first_entry;
+ e = _first_entry;
while (e != NULL) {
- stream = e->open_stream(name, CHECK_NULL);
+ stream = e->open_stream(file_name, CHECK_NULL);
+ if (!context.check(stream, classpath_index)) {
+ return h; // NULL
+ }
if (stream != NULL) {
break;
}
e = e->next();
++classpath_index;
}
}
- instanceKlassHandle h;
if (stream != NULL) {
-
// class file found, parse it
ClassFileParser parser(stream);
ClassLoaderData* loader_data = ClassLoaderData::the_null_class_loader_data();
Handle protection_domain;
TempNewSymbol parsed_name = NULL;
instanceKlassHandle result = parser.parseClassFile(h_name,
loader_data,
protection_domain,
parsed_name,
- false,
- CHECK_(h));
-
- // add to package table
- if (add_package(name, classpath_index, THREAD)) {
- h = result;
+ context.should_verify(classpath_index),
+ THREAD);
+ if (HAS_PENDING_EXCEPTION) {
+ ResourceMark rm;
+ if (DumpSharedSpaces) {
+ tty->print_cr("Preload Error: Failed to load %s", class_name);
+ }
+ return h;
+ }
+ h = context.record_result(classpath_index, e, result, THREAD);
+ } else {
+ if (DumpSharedSpaces) {
+ tty->print_cr("Preload Error: Cannot find %s", class_name);
}
}
return h;
}
@@ -1018,18 +1204,31 @@
}
}
// lookup zip library entry points
load_zip_library();
+#if INCLUDE_CDS
// initialize search path
+ if (DumpSharedSpaces) {
+ _shared_paths_misc_info = SharedClassUtil::allocate_shared_paths_misc_info();
+ }
+#endif
setup_bootstrap_search_path();
if (LazyBootClassLoader) {
// set up meta index which makes boot classpath initialization lazier
- setup_meta_index();
+ setup_bootstrap_meta_index();
}
}
+#if INCLUDE_CDS
+void ClassLoader::initialize_shared_path() {
+ if (DumpSharedSpaces) {
+ ClassLoaderExt::setup_search_paths();
+ _shared_paths_misc_info->write_jint(0); // see comments in SharedPathsMiscInfo::check()
+ }
+}
+#endif
jlong ClassLoader::classloader_time_ms() {
return UsePerfData ?
Management::ticks_to_ms(_perf_accumulated_time->get_value()) : -1;
}