< prev index next >
src/share/vm/memory/metaspaceShared.cpp
Print this page
@@ -1,7 +1,7 @@
/*
- * Copyright (c) 2012, 2016, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2012, 2017, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
@@ -38,12 +38,17 @@
#include "interpreter/bytecodes.hpp"
#include "memory/filemap.hpp"
#include "memory/metaspace.hpp"
#include "memory/metaspaceShared.hpp"
#include "memory/resourceArea.hpp"
+#include "oops/instanceClassLoaderKlass.hpp"
+#include "oops/instanceMirrorKlass.hpp"
+#include "oops/instanceRefKlass.hpp"
+#include "oops/objArrayKlass.hpp"
#include "oops/objArrayOop.hpp"
#include "oops/oop.inline.hpp"
+#include "oops/typeArrayKlass.hpp"
#include "runtime/timerTrace.hpp"
#include "runtime/os.hpp"
#include "runtime/signature.hpp"
#include "runtime/vmThread.hpp"
#include "runtime/vm_operations.hpp"
@@ -231,85 +236,260 @@
}
}
}
}
-// Patch C++ vtable pointer in metadata.
-
-// Klass and other metadata objects contain references to c++ vtables in the
-// JVM library.
-// Fix them to point to our constructed vtables. However, don't iterate
-// across the space while doing this, as that causes the vtables to be
-// patched, undoing our useful work. Instead, iterate to make a list,
-// then use the list to do the fixing.
+// Objects of the Metadata types (such as Klass and ConstantPool) have C++ vtables.
+// (In GCC this is the field <Type>::_vptr, i.e., first word in the object.)
+//
+// Addresses of the vtables and the methods may be different across JVM runs,
+// if libjvm.so is dynamically loaded at a different base address.
//
-// Our constructed vtables:
-// Dump time:
-// 1. init_self_patching_vtbl_list: table of pointers to current virtual method addrs
-// 2. generate_vtable_methods: create jump table, appended to above vtbl_list
-// 3. patch_klass_vtables: for Klass list, patch the vtable entry in klass and
-// associated metadata to point to jump table rather than to current vtbl
-// Table layout: NOTE FIXED SIZE
-// 1. vtbl pointers
-// 2. #Klass X #virtual methods per Klass
-// 1 entry for each, in the order:
-// Klass1:method1 entry, Klass1:method2 entry, ... Klass1:method<num_virtuals> entry
-// Klass2:method1 entry, Klass2:method2 entry, ... Klass2:method<num_virtuals> entry
-// ...
-// Klass<vtbl_list_size>:method1 entry, Klass<vtbl_list_size>:method2 entry,
-// ... Klass<vtbl_list_size>:method<num_virtuals> entry
-// Sample entry: (Sparc):
-// save(sp, -256, sp)
-// ba,pt common_code
-// mov XXX, %L0 %L0 gets: Klass index <<8 + method index (note: max method index 255)
+// To ensure that the Metadata objects in the CDS archive always have the correct vtable:
//
-// Restore time:
-// 1. initialize_shared_space: reserve space for table
-// 2. init_self_patching_vtbl_list: update pointers to NEW virtual method addrs in text
+// + at dump time: we redirect the _vptr to point to our own vtables inside
+// the CDS image
+// + at run time: we clone the actual contents of the vtables from libjvm.so
+// into our own tables.
//
-// Execution time:
-// First virtual method call for any object of these metadata types:
-// 1. object->klass
-// 2. vtable entry for that klass points to the jump table entries
-// 3. branches to common_code with %O0/klass, %L0: Klass index <<8 + method index
-// 4. common_code:
-// Get address of new vtbl pointer for this Klass from updated table
-// Update new vtbl pointer in the Klass: future virtual calls go direct
-// Jump to method, using new vtbl pointer and method index
-
-
-static void* find_matching_vtbl_ptr(void** vtbl_list, void* new_vtable_start, void* obj) {
- void* old_vtbl_ptr = *(void**)obj;
- for (int i = 0; i < MetaspaceShared::vtbl_list_size; i++) {
- if (vtbl_list[i] == old_vtbl_ptr) {
- return (void**)new_vtable_start + i * MetaspaceShared::num_virtuals;
+// We conservatively estimate that each class's vtable has less than 150 entries. See
+// CppVtabCloner::MAX_VTABLE_SIZE
+
+// Currently, the archive contain ONLY the following types of objects that have C++ vtables.
+#define CPP_VTAB_PATCH_TYPES_DO(f) \
+ f(ConstantPool) \
+ f(InstanceKlass) \
+ f(InstanceClassLoaderKlass) \
+ f(InstanceMirrorKlass) \
+ f(InstanceRefKlass) \
+ f(Method) \
+ f(ObjArrayKlass) \
+ f(TypeArrayKlass)
+
+#ifndef PRODUCT
+template <class T> class CppVtabTesterB: public T {
+public:
+ virtual int last_virtual_method() {return 1;}
+};
+
+template <class T> class CppVtabTesterA : public T {
+public:
+ virtual void* last_virtual_method() {
+ // Make this different than CppVtabTesterB::last_virtual_method so the C++
+ // compiler/linker won't alias the two functions.
+ return NULL;
}
+};
+#endif
+
+class CppVtabInfo {
+ intptr_t _vtab_size;
+ intptr_t _vtab[1];
+public:
+ static int num_slots(int vtab_size) {
+ return 1 + vtab_size; // Need to add the space occupied by _vtab_size;
}
- ShouldNotReachHere();
- return NULL;
+ int vtab_size() { return int(uintx(_vtab_size)); }
+ void set_vtab_size(int n) { _vtab_size = intptr_t(n); }
+ intptr_t* vtab() { return &_vtab[0]; }
+ void zero() { memset(_vtab, 0, sizeof(intptr_t) * vtab_size()); }
+};
+
+template <class T> class CppVtabCloner : public T {
+ static intptr_t* vtab_of(Metadata& m) {
+ return *((intptr_t**)&m);
+ }
+ // Currently we have no more than 120 virtual methods for all
+ // Metadata subclasses for all platforms. If you add more virtual
+ // method you will trigger the assert in verify_sufficient_size().
+ const static int MAX_VTABLE_SIZE = 150;
+ static CppVtabInfo* _info;
+
+#ifndef PRODUCT
+ // To determine the size of the vtable for each type, we use the following
+ // trick by declaring 2 subclasses:
+ //
+ // class CppVtabTesterA: public InstanceKlass {virtual int last_virtual_method() {return 1;} };
+ // class CppVtabTesterB: public InstanceKlass {virtual void* last_virtual_method() {return NULL}; };
+ //
+ // CppVtabTesterA and CppVtabTesterB's vtables have the following properties:
+ // - Their size (N+1) is exactly one more than the size of InstanceKlass's vtable (N)
+ // - The first N entries have are exactly the same as in InstanceKlass's vtable.
+ // - Their last entry is different.
+ //
+ // So to determine the value of N, we just walk CppVtabTesterA and CppVtabTesterB's tables
+ // and find the first entry that's different.
+ //
+ // This works on all C++ compilers supported by Oracle, but you may need to tweak it for more
+ // esoteric compilers.
+
+ static void verify_sufficient_size(const char* name) {
+ CppVtabTesterA<T> a;
+ CppVtabTesterB<T> b;
+
+ intptr_t* avtab = vtab_of(a);
+ intptr_t* bvtab = vtab_of(b);
+
+ // Start at slot 1, because slot 0 may be RTTI (on Solaris/Sparc)
+ int i;
+ for (i=1; ; i++) {
+ if (avtab[i] != bvtab[i]) {
+ break;
+ }
+ }
+ if (PrintSharedSpaces) {
+ tty->print_cr("%s has %d virtual methods", name, i);
+ }
+ if (i > MAX_VTABLE_SIZE) {
+ tty->print_cr("The C++ vtable size of %s (%d) is larger than the hard-coded default %d",
+ name, i, MAX_VTABLE_SIZE);
+ assert(0, "Please increase CppVtabCloner<T>::MAX_VTABLE_SIZE");
+ }
+ }
+#endif
+
+public:
+ static intptr_t* allocate(const char* name, intptr_t* md_top, intptr_t* md_end) {
+ DEBUG_ONLY(verify_sufficient_size(name));
+ int n = MAX_VTABLE_SIZE;
+ intptr_t* next = md_top + CppVtabInfo::num_slots(n);
+
+ if (next > md_end) {
+ report_out_of_shared_space(SharedMiscData);
+ }
+
+ _info = (CppVtabInfo*)md_top;
+ _info->set_vtab_size(n);
+
+ T tmp;
+ intptr_t* srcvtab = vtab_of(tmp);
+ intptr_t* dstvtab = _info->vtab();
+
+ // It is not safe to call memcpy(), because srcvtab may be shorter than MAX_VTABLE_SIZE, and
+ // may be at the last page of an addressable space. Crossing over to the next page would
+ // cause a page fault.
+ if (!CanUseSafeFetchN()) {
+ vm_exit_during_initialization("CanUseSafeFetchN() must be true to enable CDS");
+ }
+
+ for (int i=0; i<n; i++) {
+ const intptr_t bad = intptr_t(0xdeadbeef);
+ intptr_t num = SafeFetchN(&srcvtab[i], bad);
+ if (num == bad
+ // || i > 120 /* uncomment this line to test */
+ ) {
+ _info->set_vtab_size(i-1);
+ break;
+ }
+ dstvtab[i] = num;
+ }
+
+ return md_top + CppVtabInfo::num_slots(_info->vtab_size());
+ }
+
+ static intptr_t* clone_vtable(const char* name, intptr_t* p) {
+ T tmp;
+ CppVtabInfo* info = (CppVtabInfo*)p;
+ int n = info->vtab_size();
+ intptr_t* srcvtab = vtab_of(tmp);
+ intptr_t* dstvtab = info->vtab();
+
+ // We already checked (and, if necessary, adjusted n) when the vtables were allocated, so we are
+ // safe to do memcpy.
+ if (PrintSharedSpaces) {
+ tty->print_cr("%s copying %d vtable entries", name, n);
+ }
+ memcpy(dstvtab, srcvtab, sizeof(intptr_t) * n);
+ return dstvtab + n;
+ }
+
+ static void zero_vtable_clone() {
+ assert(DumpSharedSpaces, "dump-time only");
+ _info->zero();
+ }
+
+ static void patch(Metadata* obj) {
+ assert(DumpSharedSpaces, "dump-time only");
+ *(void**)obj = (void*)(_info->vtab());
+ }
+};
+
+template <class T> CppVtabInfo* CppVtabCloner<T>::_info = NULL;
+
+
+#define ALLOC_CPP_VTAB_CLONE(c) \
+ md_top = CppVtabCloner<c>::allocate(#c, md_top, md_end);
+
+#define CLONE_CPP_VTABLE(c) \
+ p = CppVtabCloner<c>::clone_vtable(#c, p);
+
+#define ZERO_CPP_VTABLE(c) \
+ CppVtabCloner<c>::zero_vtable_clone();
+
+
+intptr_t* MetaspaceShared::allocate_cpp_vtable_clones(intptr_t* md_top, intptr_t* md_end) {
+ assert(DumpSharedSpaces, "dump-time only");
+ CPP_VTAB_PATCH_TYPES_DO(ALLOC_CPP_VTAB_CLONE);
+ return md_top;
}
-// Assumes the vtable is in first slot in object.
-static void patch_klass_vtables(void** vtbl_list, void* new_vtable_start) {
+// This can be called at both dump time and run time.
+intptr_t* MetaspaceShared::clone_cpp_vtables(intptr_t* p) {
+ assert(DumpSharedSpaces || UseSharedSpaces, "sanity");
+ CPP_VTAB_PATCH_TYPES_DO(CLONE_CPP_VTABLE);
+ return p;
+}
+
+void MetaspaceShared::zero_cpp_vtable_clones_for_writing() {
+ assert(DumpSharedSpaces, "dump-time only");
+ CPP_VTAB_PATCH_TYPES_DO(ZERO_CPP_VTABLE);
+}
+
+intptr_t* MetaspaceShared::init_cpp_vtable_clones(intptr_t* md_top, intptr_t* md_end) {
+ assert(DumpSharedSpaces, "dump-time only");
+ // Layout (each slot is a intptr_t):
+ // [number of slots in the first vtable = n1]
+ // [ <n1> slots for the first vtable]
+ // [number of slots in the first second = n2]
+ // [ <n2> slots for the second vtable]
+ // ...
+ // The order of the vtables is the same as the CPP_VTAB_PATCH_TYPES_DO macro.
+
+ intptr_t* start = md_top;
+ md_top = allocate_cpp_vtable_clones(md_top, md_end);
+ return md_top;
+}
+
+// Assumes the vtable pointer is in first slot in object.
+void MetaspaceShared::patch_cpp_vtable_pointers() {
int n = _global_klass_objects->length();
for (int i = 0; i < n; i++) {
Klass* obj = _global_klass_objects->at(i);
// Note is_instance_klass() is a virtual call in debug. After patching vtables
// all virtual calls on the dummy vtables will restore the original!
if (obj->is_instance_klass()) {
InstanceKlass* ik = InstanceKlass::cast(obj);
- *(void**)ik = find_matching_vtbl_ptr(vtbl_list, new_vtable_start, ik);
+ if (ik->is_class_loader_instance_klass()) {
+ CppVtabCloner<InstanceClassLoaderKlass>::patch(ik);
+ } else if (ik->is_reference_instance_klass()) {
+ CppVtabCloner<InstanceRefKlass>::patch(ik);
+ } else if (ik->is_mirror_instance_klass()) {
+ CppVtabCloner<InstanceMirrorKlass>::patch(ik);
+ } else {
+ CppVtabCloner<InstanceKlass>::patch(ik);
+ }
ConstantPool* cp = ik->constants();
- *(void**)cp = find_matching_vtbl_ptr(vtbl_list, new_vtable_start, cp);
+ CppVtabCloner<ConstantPool>::patch(cp);
for (int j = 0; j < ik->methods()->length(); j++) {
Method* m = ik->methods()->at(j);
- *(void**)m = find_matching_vtbl_ptr(vtbl_list, new_vtable_start, m);
+ CppVtabCloner<Method>::patch(m);
}
+ } else if (obj->is_objArray_klass()) {
+ CppVtabCloner<ObjArrayKlass>::patch(obj);
} else {
- // Array klasses
- Klass* k = obj;
- *(void**)k = find_matching_vtbl_ptr(vtbl_list, new_vtable_start, k);
+ assert(obj->is_typeArray_klass(), "sanity");
+ CppVtabCloner<TypeArrayKlass>::patch(obj);
}
}
}
// Closure for serializing initialization data out to a data area to be
@@ -339,11 +519,11 @@
*top = (intptr_t)*p;
++top;
}
void do_u4(u4* p) {
- void* ptr = (void*)(uintx(*p));
+ void* ptr = (void*)(intptr_t(*p));
do_ptr(&ptr);
}
void do_tag(int tag) {
check_space();
@@ -620,28 +800,16 @@
char* mc_end = _mc_vs.high();
char* od_low = _od_vs.low();
char* od_top = MetaspaceShared::optional_data_region()->alloc_top();
char* od_end = _od_vs.high();
- // Reserve space for the list of Klass*s whose vtables are used
- // for patching others as needed.
-
- void** vtbl_list = (void**)md_top;
- int vtbl_list_size = MetaspaceShared::vtbl_list_size;
- Universe::init_self_patching_vtbl_list(vtbl_list, vtbl_list_size);
+ char* vtbl_list = md_top;
+ md_top = (char*)MetaspaceShared::init_cpp_vtable_clones((intptr_t*)md_top, (intptr_t*)md_end);
- md_top += vtbl_list_size * sizeof(void*);
- void* vtable = md_top;
-
- // Reserve space for a new dummy vtable for klass objects in the
- // heap. Generate self-patching vtable entries.
-
- MetaspaceShared::generate_vtable_methods(vtbl_list, &vtable,
- &md_top, md_end,
- &mc_top, mc_end);
-
- guarantee(md_top <= md_end, "Insufficient space for vtables.");
+ // We don't use MC section anymore. We will remove it in a future RFE. For now, put one
+ // byte inside so the region writing/mapping code works.
+ mc_top ++;
// Reorder the system dictionary. (Moving the symbols affects
// how the hash table indices are calculated.)
// Not doing this either.
@@ -711,24 +879,21 @@
tty->print_cr(fmt_space, "st", ss_bytes, ss_t_perc, ss_bytes, 100.0, p2i(ss_low));
tty->print_cr(fmt_space, "od", od_bytes, od_t_perc, od_alloced, od_u_perc, p2i(od_low));
tty->print_cr("total : " SIZE_FORMAT_W(9) " [100.0%% of total] out of " SIZE_FORMAT_W(9) " bytes [%5.1f%% used]",
total_bytes, total_alloced, total_u_perc);
- // Update the vtable pointers in all of the Klass objects in the
- // heap. They should point to newly generated vtable.
- patch_klass_vtables(vtbl_list, vtable);
-
- // dunno what this is for.
- char* saved_vtbl = (char*)os::malloc(vtbl_list_size * sizeof(void*), mtClass);
- memmove(saved_vtbl, vtbl_list, vtbl_list_size * sizeof(void*));
- memset(vtbl_list, 0, vtbl_list_size * sizeof(void*));
+ MetaspaceShared::patch_cpp_vtable_pointers();
+
+ // The vtable clones contain addresses of the current process.
+ // We don't want to write addresses these into the archive.
+ MetaspaceShared::zero_cpp_vtable_clones_for_writing();
// Create and write the archive file that maps the shared spaces.
FileMapInfo* mapinfo = new FileMapInfo();
mapinfo->populate_header(MetaspaceShared::max_alignment());
- mapinfo->set_misc_data_patching_start((char*)vtbl_list);
+ mapinfo->set_misc_data_patching_start(vtbl_list);
mapinfo->set_cds_i2i_entry_code_buffers(MetaspaceShared::cds_i2i_entry_code_buffers());
mapinfo->set_cds_i2i_entry_code_buffers_size(MetaspaceShared::cds_i2i_entry_code_buffers_size());
for (int pass=1; pass<=2; pass++) {
if (pass == 1) {
@@ -759,12 +924,12 @@
true, false);
}
mapinfo->close();
- memmove(vtbl_list, saved_vtbl, vtbl_list_size * sizeof(void*));
- os::free(saved_vtbl);
+ // Restore the vtable in case we invoke any virtual methods.
+ MetaspaceShared::clone_cpp_vtables((intptr_t*)vtbl_list);
if (PrintSharedSpaces) {
DumpAllocClosure dac;
dac.iterate_metaspace(_loader_data->ro_metaspace(), DumpAllocClosure::RO);
dac.iterate_metaspace(_loader_data->rw_metaspace(), DumpAllocClosure::RW);
@@ -1136,23 +1301,11 @@
FileMapInfo *mapinfo = FileMapInfo::current_info();
_cds_i2i_entry_code_buffers = mapinfo->cds_i2i_entry_code_buffers();
_cds_i2i_entry_code_buffers_size = mapinfo->cds_i2i_entry_code_buffers_size();
char* buffer = mapinfo->misc_data_patching_start();
- // Skip over (reserve space for) a list of addresses of C++ vtables
- // for Klass objects. They get filled in later.
-
- void** vtbl_list = (void**)buffer;
- buffer += MetaspaceShared::vtbl_list_size * sizeof(void*);
- Universe::init_self_patching_vtbl_list(vtbl_list, vtbl_list_size);
-
- // Skip over (reserve space for) dummy C++ vtables Klass objects.
- // They are used as is.
-
- intptr_t vtable_size = *(intptr_t*)buffer;
- buffer += sizeof(intptr_t);
- buffer += vtable_size;
+ buffer = (char*)clone_cpp_vtables((intptr_t*)buffer);
int sharedDictionaryLen = *(intptr_t*)buffer;
buffer += sizeof(intptr_t);
int number_of_entries = *(intptr_t*)buffer;
buffer += sizeof(intptr_t);
< prev index next >