Proposal for improving CDS archive creation
Jiangli Zhou
jiangli.zhou at oracle.com
Fri Jul 13 19:43:14 UTC 2018
On 7/13/18 11:50 AM, Ioi Lam wrote:
> When writing into the buffer, the algorithm works like this
>
>
> MetaspaceObj* get_buffered(MetaspaceObj *p) {
> MetaspaceObj* saved = buffer_find(p);
> if (saved == NULL) {
> saved = buffer_write(p);
> }
> }
>
> So when you're writing a vtable into the buffer:
>
> Method** vtable = ...; // points to the "real" class X
> Method** vtable_buffered = ...; // points to the "buffered" class X
>
> for (int i=0; i<vtable_length; i++) {
> Method* m = vtable[i];
> Method* buffered_m = get_buffered(m);
> vtable_buffered[i] = buffered_m;
> }
>
> buffer_write(m) will not happen if m is a method defined by a super
> class of X.
>
> However, with some class are unloaded and the metaspace blocks are
> being reused, a new MetaspaceObject may happen to occupy the exact
> same address as an old MetaspaceObject from an unloaded class. This
> would make the buffering operation more complicated.
>
> We have 2 choices:
>
> [1] Disable the deallocation of MetaspaceObjects when
> -Xshare:autocreate is specified.
> [2] When a MetaspaceObject is deallocated, remove it from the hash
> table used by buffer_find().
>
> We can start with [1] as it has a lesser chance of working
> incorrectly, (except it might run out of metaspace memory for some
> pathological cases).
That sounds reasonable to me. Could you please also add a note to the
RFE report so we can keep track the design decision.
Thanks,
Jiangli
>
>
> - Ioi
>
> On 7/11/18 5:46 PM, Jiangli Zhou wrote:
>> Volker originally suggested the idea in the email thread "Improving
>> AppCDS for Custom Loaders". I think this is a cleaner approach.
>>
>> Thanks,
>>
>> Jiangli
>>
>>
>> On 7/11/18 4:13 PM, Ioi Lam wrote:
>>> I had an off-line discussion with Jiangli, and she has an
>>> alternative proposal:
>>>
>>> When -Xshare:autocreate is specified, but the CDS archive is not
>>> available,
>>>
>>> 1. Load classes as normal. After each InstanceKlass is loaded, but
>>> before it's used,
>>> make a deep copy of this class into an internal cache.
>>>
>>> 2. The deep copy includes all methods, etc, for this class. However,
>>> if a Method is
>>> inherited from a super class, then only a reference to this
>>> Method is copied.
>>>
>>> 3. At a certain point (probably at VM exit), copy all the (suitable)
>>> classes from the
>>> cache and write them into the CDS archive.
>>>
>>> The advantage of this approach is we will be able to archive classes
>>> that were
>>> loaded by custom loaders, but have been freed at VM exit time
>>> because the class
>>> loaders were GC'ed.
>>>
>>>
>>> Note: When a class X is loaded, if its supertype(s) have already
>>> been redefined,
>>> we probably should not copy X into the buffer. That's because the
>>> vtable of X may
>>> point to some redefined methods from a supertype, which do not match
>>> the bytecodes
>>> of these methods in the supertype's original class file, so it's a
>>> messy situation.
>>>
>>> Thanks
>>> - Ioi
>>>
>>>
>>>
>>> On 7/10/18 12:50 PM, Ioi Lam wrote:
>>>> Fixing some sloppy text below ....
>>>>
>>>>
>>>> On 7/10/18 10:16 AM, Ioi Lam wrote:
>>>>> I have a proposal for improving the process of creating of the CDS
>>>>> archive(s),
>>>>> so we can make CDS easier to use and support more use cases.
>>>>>
>>>>> - better support for custom loaders
>>>>> - remove explicit training run
>>>>> - support 2 levels of shared archives
>>>>>
>>>>> I think the proposal is relatively straight-forward to implement,
>>>>> as we already
>>>>> have most of the required infrastructures:
>>>>>
>>>>> + the ability to use Java class loaders at archive creation time
>>>>> + the ability to relocate MetaspaceObjects
>>>>>
>>>>> Parts of this proposal will also simplify the CDS code and make it
>>>>> more
>>>>> maintainable.
>>>>>
>>>>> Current process of creating the base archive - [C]
>>>>> ==================================================
>>>>>
>>>>> Currently each JVM process can map at most one CDS archive. Let's
>>>>> call this
>>>>> the "base archive". It is created by [ref1]:
>>>>>
>>>>> C1. Reserve a region R of 3GB at 0x800000000.
>>>>> C2. Load all classes specified in the class list. All data for
>>>>> these classes
>>>>> live outside of R.
>>>>> (E.g., the Klass objects are loaded into tmp_class_space,
>>>>> which is
>>>>> adjacent to R).
>>>>> C3. Copy the metadata of all archivable classes (e.g, exclude
>>>>> generated
>>>>> Lambda classes) into R. At this step, R is divided into several
>>>>> sections (RO, RW, etc).
>>>>>
>>>>>
>>>>> // +-- SharedBaseAddress (default = 0x800000000)
>>>>> // +-- _narrow_klass._base
>>>>> // |
>>>>> // | +-tmp_class_space.base
>>>>> // v V
>>>>> // +----+----+----+----+----+-....-+-------------------+
>>>>> // |<- R ->|
>>>>> // | MC | RW | RO | MD | OD |unused| tmp_class_space |
>>>>> // +----+----+----+----+----+------+-------------------+
>>>>> // |<-- 3GB -------------->|
>>>>> // |<-- UnscaledClassSpaceMax = 4GB ------------------>|
>>>>>
>>>>>
>>>>> New process for creating the base archive - [N]
>>>>> ===============================================
>>>>>
>>>>> Currently we have a lot of "if (DumpSharedSpaces)" code to for
>>>>> special case
>>>>> handling of the above scheme. We can improve it by
>>>>>
>>>>> N1. Remove all code for special memory layout initialization for
>>>>> -Xshare:dump.
>>>>> As a result, we will reserve a region R of 1GB at
>>>>> 0x800000000, which
>>>>> is used by Klass objects (this is the same as if -Xshare:off
>>>>> were
>>>>> specified.)
>>>>> N2. Load all classes in the class list.
>>>>> N3. Now R contains the Klass objects of all loaded classes.
>>>>> Allocate a temporary space T, and copy all contents of R into T.
>>>>> N4. Now R is empty. Copy the metadata of all archivable classes
>>>>> into R.
>>>>>
>>>>>
>>>>> Dump-as-you-go for the base archive - [G]
>>>>> =========================================
>>>>>
>>>>> Note that the [N] scheme will work even if you're running an app with
>>>>> -Xshare:off. At some point (e.g., when the VM is about to exit), you
>>>>> can:
>>>>>
>>>>> G1. Enter a safe point
>>>>> G2. Go to step [N3].
>>>>>
>>>>> The benefit of [G] is you don't need a separate run to dump the
>>>>> archive, and
>>>>> there's no need to use the class list. Instead, we can have an
>>>>> option like:
>>>>>
>>>>> java -Xshare:autocreate -cp app.jar
>>>>> -XX:SharedArchiveFile=foo.jsa App
>>>>>
>>>>> If foo.jsa is not available, we run in [G] mode. At VM exit, we
>>>>> dump into
>>>>> foo.jsa.
>>>>>
>>>>> This way, we don't need to have an explicit training run with
>>>>> -XX:DumpLoadedClassList. Instead, the training run is
>>>>>
>>>> I meant, "Instead, your first run, when the archive is not yet
>>>> available, becomes the
>>>> training run".
>>>>
>>>> Thanks to Calvin and Dan for spotting this :-)
>>>> - Ioi
>>>>
>>>>> This also makes it easy to support the classes from custom
>>>>> loaders. There's no
>>>>> need for special tooling to convert -Xlog:class+load=debug output
>>>>> into a
>>>>> classlist. [ref2]
>>>>>
>>>>>
>>>>> Dumping for second-level archive - [S]
>>>>> ======================================
>>>>>
>>>>> S1. Load the base archive
>>>>> S2. Run the app as normal
>>>>> S3. All Klass objects of the dynamically loaded classes will be
>>>>> loaded in
>>>>> the region R, which immediately follows the end of the base
>>>>> archive.
>>>>>
>>>>> // +-- SharedBaseAddress
>>>>> // | +--- dynamically loaded Klasses
>>>>> // | | start from here.
>>>>> // v v
>>>>> // +--------------------------+---------...-----------------|
>>>>> // | base archive | region R |
>>>>> // +--------------------------+---------...-----------------|
>>>>> // |<- size of base archive ->|
>>>>> // |<-- 1GB -->|
>>>>>
>>>>>
>>>>> S4. At some point (possible when the VM is about to exit) we start
>>>>> dumping the second level archive
>>>>> S5. Enter safe point
>>>>> S6. Now R contains the Klass objects of all dynamically loaded
>>>>> classes.
>>>>> Allocate a temporary space T, and copy all contents of R
>>>>> into T.
>>>>> S7. Now R is empty. Copy the metadata of all archivable,
>>>>> dynamically loaded
>>>>> classes into R.
>>>>> S8. Create a new shared_dictionary (and shared_symbol_table)
>>>>> that contains
>>>>> all the Klasses (Symbols) from both the base and
>>>>> second-level archives.
>>>>>
>>>>> References
>>>>> ==========
>>>>>
>>>>> [ref1] Current initialization of memory space layout during
>>>>> -Xshare:dump
>>>>> http://hg.openjdk.java.net/jdk/jdk/file/e0028bb6dd3d/src/hotspot/share/memory/metaspaceShared.cpp#l250
>>>>>
>>>>> [ref2] Volker Simonis's tool for support custom class loaders in CDS
>>>>> https://github.com/simonis/cl4cds
>>>>> ----------------------------------------------------------------------
>>>>>
>>>>>
>>>>>
>>>>>
>>>>> Any thoughts?
>>>>>
>>>>> Thanks
>>>>> - Ioi
>>>>
>>>
>>
>
More information about the hotspot-runtime-dev
mailing list