Index: vm/vmcore/include/jarfile_support.h =================================================================== --- vm/vmcore/include/jarfile_support.h (revision 628638) +++ vm/vmcore/include/jarfile_support.h (working copy) @@ -94,6 +94,8 @@ typedef long JarCompressedOffset; +class JarFile; + class JarEntry { unsigned short m_version; @@ -109,7 +111,6 @@ protected: JarCompressedOffset m_contentOffset; -// std::string m_jarFileName; int m_jarFileIdx; public: @@ -121,7 +122,7 @@ // returns decompressed content in provided buffer; // buffer must have at least GetContentSize() bytes length // NB! upon erroneous return from GetContent user cannot rely on buffer content - bool GetContent( unsigned char* content, JarFile *jf ) const; + bool GetContent(unsigned char* content) const; // construct fixed part of JarEntryHeader class from input stream void ConstructFixed( const unsigned char* stream ); // verify that "stream" starts with JAR_DIRECTORYENTRY_MAGIC signature @@ -143,34 +144,14 @@ protected: friend class JarFile; + friend class JarEntryCache; }; // class JarEntry -class JarFile -{ - int m_jarFileIdx; +class JarEntryCache { +private: std::multimap m_entries; - Manifest* m_manifest; - tl::MemoryPool pool; - // list of the jar files - static std::vector m_jars; - -public: - JarFile() : m_manifest(NULL), jfh(0) {} - JarFile( const JarFile& jf ) : jfh(0) { - m_jarFileIdx = jf.m_jarFileIdx; - m_entries = jf.m_entries; - m_manifest = new Manifest( &jf ); - } - ~JarFile() { - if (jfh != 0) close(jfh); - jfh = 0; - if( m_manifest ) delete m_manifest; - m_manifest = NULL; - } - // parses JAR file and stores its structure inside - bool Parse( const char* filename ); // get string hash value inline int GetHashValue( const char* je_name ) const{ // hash fuction is taken from String_Pool class @@ -188,7 +169,13 @@ hash = (h1 + (h2 << 8)) & 0x7fffffff; return hash; } - // looks up JAR entry in stored jar structure + +public: + // inserts new entry in this cache + void Insert(JarEntry& je) { + m_entries.insert(std::make_pair(GetHashValue(je.m_fileName), je)); + } + // looks up JAR entry in this cache const JarEntry* Lookup( const char* je_name ) const { const int hash = GetHashValue(je_name); std::multimap::const_iterator it = m_entries.find(hash); @@ -211,11 +198,57 @@ return NULL; } +}; + + +class JarFile +{ + const char* m_name; + JarEntryCache* m_entries; + bool m_ownCache; + Manifest* m_manifest; + tl::MemoryPool pool; + + // list of the jar files + static std::vector m_jars; + +public: + JarFile(JarEntryCache* jec = NULL) + : m_name(NULL), m_entries(jec), m_ownCache(jec == NULL), m_manifest(NULL), jfh(0) {} + JarFile( const JarFile& jf ) : jfh(0) { + m_name = jf.m_name; + m_entries = jf.m_entries; + m_manifest = new Manifest(jf.m_manifest); + } + ~JarFile() { + if(m_ownCache) + m_entries->~JarEntryCache(); + m_ownCache = false; + m_entries = NULL; + if (jfh != 0) close(jfh); + jfh = 0; + if( m_manifest ) delete m_manifest; + m_manifest = NULL; + } + // parses JAR file and stores its structure inside + bool Parse( const char* filename ); + // returns entry cache (either shared or owned) + JarEntryCache* GetCache() { return m_entries; } + // returns if this JarFile owns entry cache it holds + bool HasSharedCache() { return !m_ownCache; } + const JarEntry* Lookup( const char* je_name ) const { + return m_entries->Lookup(je_name); + } // returns manifest from parsed jar archive - Manifest* Get_Manifest() { return m_manifest; } + Manifest* GetManifest() { return m_manifest; } // returns JAR file name - const char* GetName() { return m_jars[m_jarFileIdx].c_str(); } + const char* GetName() { return m_name; } + // Return jar from jars array + static JarFile* GetJar(int idx) { + return m_jars[idx]; + } + // handle of the jar file int jfh; Lock_Manager lock; Index: vm/vmcore/include/jarfile_util.h =================================================================== --- vm/vmcore/include/jarfile_util.h (revision 628638) +++ vm/vmcore/include/jarfile_util.h (working copy) @@ -62,7 +62,7 @@ assert(jarfl); // get archive manifest - Manifest *manifest = jarfl->Get_Manifest(); + Manifest *manifest = jarfl->GetManifest(); if(!manifest) { return NULL; } Index: vm/vmcore/include/manifest.h =================================================================== --- vm/vmcore/include/manifest.h (revision 628638) +++ vm/vmcore/include/manifest.h (working copy) @@ -23,6 +23,7 @@ #include "properties.h" +class JarEntry; class JarFile; class Manifest @@ -30,9 +31,11 @@ Properties m_main; bool m_parsed; public: - Manifest( const JarFile* jf ); + Manifest() {} + Manifest(const Manifest* mnf); ~Manifest(); + bool Parse(const JarEntry* mje); Properties* GetMainProperties() { return &m_main; } bool operator! () { return !m_parsed; } Index: vm/vmcore/include/environment.h =================================================================== --- vm/vmcore/include/environment.h (revision 628638) +++ vm/vmcore/include/environment.h (working copy) @@ -90,6 +90,13 @@ Lock_Manager *p_suspend_lock; /** + * If set to true, DLRVM will store JARs which are adjacent in boot class path + * into single jar entry cache. This will optimize lookups on class loading + * with bootstrap class loader. + */ + bool use_common_jar_cache; + + /** * If set to true by the -compact_fields command-line option, * the VM will not pad out fields of less than 32 bits to four bytes. * However, fields will still be aligned to a natural boundary, Index: vm/vmcore/include/Class.h =================================================================== --- vm/vmcore/include/Class.h (revision 628638) +++ vm/vmcore/include/Class.h (working copy) @@ -826,7 +826,7 @@ int m_is_suitable_for_fast_instanceof; // string name of file from which this class has been loaded - const String* m_class_file_name; + const char* m_class_file_name; // string name of source java file from which this class has been compiled const String* m_src_file_name; @@ -1538,7 +1538,7 @@ * or to be removed altogether, if possible. * Sets the name of a file from which the given class has been loaded. * @param[in] cf_name - a class-file name*/ - void set_class_file_name(const String* cf_name) { + void set_class_file_name(const char* cf_name) { assert(cf_name); m_class_file_name = cf_name; } Index: vm/vmcore/src/class_support/Class.cpp =================================================================== --- vm/vmcore/src/class_support/Class.cpp (revision 628638) +++ vm/vmcore/src/class_support/Class.cpp (working copy) @@ -105,7 +105,8 @@ m_const_pool.init(); - m_class_file_name = m_src_file_name = NULL; + m_class_file_name = NULL; + m_src_file_name = NULL; m_state = ST_Start; Index: vm/vmcore/src/class_support/manifest.cpp =================================================================== --- vm/vmcore/src/class_support/manifest.cpp (revision 628638) +++ vm/vmcore/src/class_support/manifest.cpp (working copy) @@ -76,32 +76,25 @@ return (long)(dst - manifest); } -Manifest::Manifest( const JarFile* jf ) + +bool Manifest::Parse(const JarEntry* manifestJe) { + assert(manifestJe != NULL); m_parsed = false; - const JarEntry* manifestJe; char* manifest; long manifestSize; - - manifestJe = jf->Lookup( "META-INF/MANIFEST.MF" ); - if( !manifestJe ) { - // no manifest in JAR - m_parsed = true; - return; - } - + manifestSize = manifestJe->GetContentSize(); if( !manifestSize ) { - return; + return false; } manifest = new char[manifestSize + 1]; if( !manifest ) { - return; + return false; } - if( !manifestJe->GetContent( reinterpret_cast(manifest), - const_cast(jf) ) ) { + if(!manifestJe->GetContent(reinterpret_cast(manifest))) { delete[] manifest; - return; + return false; } manifest[manifestSize] = '\0'; @@ -169,6 +162,7 @@ delete[] manifest; m_parsed = true; + return true; } Index: vm/vmcore/src/class_support/classloader.cpp =================================================================== --- vm/vmcore/src/class_support/classloader.cpp (revision 628638) +++ vm/vmcore/src/class_support/classloader.cpp (working copy) @@ -1166,29 +1166,27 @@ return; } - // allocate and set a new bootsclasspath element element = (BCPElement*)apr_palloc(pool, sizeof(BCPElement) ); element->m_path = new_path; element->m_next = NULL; + element->m_isJarFile = file_is_archive(new_path->bytes); + if(element->m_isJarFile) { + JarEntryCache* jec = NULL; + if(m_env->use_common_jar_cache + &&m_BCPElements.m_last + && m_BCPElements.m_last->m_isJarFile) + { + // if previous boot class path element is jar, reuse the same cache + jec = m_BCPElements.m_last->m_jar->GetCache(); + } + void* mem_JarFile = apr_palloc(pool, sizeof(JarFile)); + element->m_jar = new (mem_JarFile) JarFile(jec); - // check if it is a archive file - if( file_is_archive(new_path->bytes) ) { - // create and parse a new archive file structure - element->m_isJarFile = true; - void* mem_JarFile = apr_palloc(pool, sizeof(JarFile) ); - element->m_jar = new (mem_JarFile) JarFile(); - if( element->m_jar && element->m_jar->Parse(new_path->bytes) ) { + if(element->m_jar && element->m_jar->Parse(new_path->bytes)) { TRACE2("classloader.jar", "opened archive: " << new_path ); - } else { - if( element->m_jar ) { - element->m_jar->~JarFile(); - } - return; } - } else { - element->m_isJarFile = false; } - + // insert element into collection if( NULL == m_BCPElements.m_first ) { m_BCPElements.m_first = element; @@ -1882,7 +1880,7 @@ Class* clss = DefineClass(m_env, class_name->bytes, buf, 0, (unsigned)buf_len); if(clss) { - clss->set_class_file_name(m_env->string_pool.lookup(full_name)); + clss->set_class_file_name(m_env->string_pool.lookup(full_name)->bytes); } STD_FREE(buf); apr_pool_destroy(local_pool); @@ -1895,6 +1893,12 @@ { assert(!exn_raised()); + if(jar_file->HasSharedCache()) { + // class was looked up earlier + *not_found = true; + return NULL; + } + // find archive entry in archive file *not_found = false; const JarEntry *entry = jar_file->Lookup(class_name_in_jar); @@ -1909,7 +1913,7 @@ unsigned char* buffer = (unsigned char*)STD_MALLOC(size); // FIXME: check that memory was allocated - if(!entry->GetContent(buffer, jar_file)) { + if(!entry->GetContent(buffer)) { // cannot unpack entry *not_found = true; STD_FREE(buffer); @@ -1923,7 +1927,7 @@ Class *clss = DefineClass(m_env, class_name->bytes, buffer, 0, size, NULL); if(clss) { // set class file name - clss->set_class_file_name(m_env->string_pool.lookup(jar_file->GetName())); + clss->set_class_file_name(jar_file->GetName()); } STD_FREE(buffer); Index: vm/vmcore/src/init/vm_init.cpp =================================================================== --- vm/vmcore/src/init/vm_init.cpp (revision 628638) +++ vm/vmcore/src/init/vm_init.cpp (working copy) @@ -761,6 +761,7 @@ // see Global_Env::Global_Env for defaults vm_env->sort_fields = get_boolean_property("vm.sort_fields", vm_env->sort_fields, VM_PROPERTIES); vm_env->compact_fields = get_boolean_property("vm.compact_fields", vm_env->compact_fields, VM_PROPERTIES); + vm_env->use_common_jar_cache = get_boolean_property("vm.common_jar_cache", TRUE, VM_PROPERTIES); vm_env->init_pools(); Index: vm/vmcore/src/util/jarfile_support.cpp =================================================================== --- vm/vmcore/src/util/jarfile_support.cpp (revision 628638) +++ vm/vmcore/src/util/jarfile_support.cpp (working copy) @@ -46,11 +46,14 @@ ((unsigned int)stream[44]<<16) + ((unsigned int)stream[45]<<24); } -std::vector JarFile::m_jars = std::vector(); -bool JarEntry::GetContent( unsigned char* content, JarFile *jf ) const +std::vector JarFile::m_jars = std::vector(); + + +bool JarEntry::GetContent(unsigned char* content) const { // length of content should be enough for storing m_size_uncompressed bytes + JarFile* jf = JarFile::GetJar(m_jarFileIdx); int inFile = jf->jfh; // gregory - It is necessary to lock per jar file because the file // handle is global to all threads, and file operations like seek, @@ -152,14 +155,14 @@ jfh = fp; if( fp == -1 ) return false; - m_jars.push_back(fileName); - JarEntry je; - m_jarFileIdx = je.m_jarFileIdx = (int)(m_jars.size() - 1); + m_name = fileName; struct stat fs; if(stat(fileName, &fs) == -1) return false; unsigned long fsize = fs.st_size; + m_jars.push_back(this); + int cd_size = fsize < 67000 ? fsize : 67000; lseek(fp, -cd_size, SEEK_END); unsigned char *buf = (unsigned char *)STD_ALLOCA(cd_size); @@ -167,6 +170,9 @@ long offsetCD; // start of the Central Dir int number; // number of entries + JarEntry je; + je.m_jarFileIdx = (int)(m_jars.size() - 1); + while (1){ if (je.IsPK(buf + off)){ if (je.IsEODMagic(buf + off + 2)){ @@ -180,6 +186,14 @@ return false; } + m_manifest = new Manifest(); + if(!m_manifest) return false; + + if(m_ownCache) { + void* jec_mem = pool.alloc(sizeof(JarEntryCache)); + m_entries = new (jec_mem) JarEntryCache(); + } + lseek(fp, offsetCD, SEEK_SET); buf = (unsigned char *)STD_MALLOC(fsize - offsetCD); @@ -195,19 +209,24 @@ strncpy(je.m_fileName, (const char *)buf + off + JAR_DIRECTORYENTRY_LEN, je.m_nameLength ); je.m_fileName[je.m_nameLength] = '\0'; je.m_contentOffset = je.m_relOffset + JarEntry::sizeFixed + je.m_nameLength + je.m_extraLength; - m_entries.insert(make_pair(GetHashValue(je.m_fileName), je)); + if(!strcmp(je.m_fileName, "META-INF/MANIFEST.MF")) { + // parse manifest + if(!m_manifest->Parse(&je)) return false; + if(!(*m_manifest)) { + delete m_manifest; + return false; + } + } else { + // Insertion in common cache preserves the order of inserted + // elements with the same hash value, so lookup will return + // the first occurrence. Although more memory is spent here, + // we do not need to lookup on each entry. + m_entries->Insert(je); + } off += JAR_DIRECTORYENTRY_LEN + je.m_nameLength + je.m_extraLength; } STD_FREE(buf); - // read and parse manifest from archive - m_manifest = new Manifest( this ); - if( !m_manifest ) return false; - if( !(*m_manifest) ) { - delete m_manifest; - return false; - } - return true; }