6964458: Reimplement class meta-data storage to use native memory

Remove PermGen, allocate meta-data in metaspace linked to class loaders, rewrite GC walking, rewrite and rename metadata to be C++ classes

Co-authored-by: Stefan Karlsson <stefan.karlsson@oracle.com>
Co-authored-by: Mikael Gerdin <mikael.gerdin@oracle.com>
Co-authored-by: Tom Rodriguez <tom.rodriguez@oracle.com>
Reviewed-by: jmasa, stefank, never, coleenp, kvn, brutisso, mgerdin, dholmes, jrose, twisti, roland
This commit is contained in:
Jon Masamitsu 2012-09-01 13:25:18 -04:00 committed by Coleen Phillimore
parent 36eee7c8c8
commit 5c58d27aac
853 changed files with 26124 additions and 82956 deletions

View file

@ -1,5 +1,5 @@
/*
* Copyright (c) 2002, 2010, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2002, 2012, 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
@ -24,6 +24,7 @@
#include "precompiled.hpp"
#include "gc_implementation/shared/cSpaceCounters.hpp"
#include "memory/metaspace.hpp"
#include "memory/resourceArea.hpp"
CSpaceCounters::CSpaceCounters(const char* name, int ordinal, size_t max_size,
@ -44,7 +45,7 @@ CSpaceCounters::CSpaceCounters(const char* name, int ordinal, size_t max_size,
PerfDataManager::create_string_constant(SUN_GC, cname, name, CHECK);
cname = PerfDataManager::counter_name(_name_space, "maxCapacity");
PerfDataManager::create_constant(SUN_GC, cname, PerfData::U_Bytes,
_max_capacity = PerfDataManager::create_variable(SUN_GC, cname, PerfData::U_Bytes,
(jlong)max_size, CHECK);
cname = PerfDataManager::counter_name(_name_space, "capacity");

View file

@ -1,5 +1,5 @@
/*
* Copyright (c) 2002, 2010, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2002, 2012, 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
@ -38,6 +38,7 @@ class CSpaceCounters: public CHeapObj<mtGC> {
private:
PerfVariable* _capacity;
PerfVariable* _used;
PerfVariable* _max_capacity;
// Constant PerfData types don't need to retain a reference.
// However, it's a good idea to document them here.
@ -55,15 +56,15 @@ class CSpaceCounters: public CHeapObj<mtGC> {
if (_name_space != NULL) FREE_C_HEAP_ARRAY(char, _name_space, mtInternal);
}
inline void update_capacity() {
virtual inline void update_capacity() {
_capacity->set_value(_space->capacity());
}
inline void update_used() {
virtual inline void update_used() {
_used->set_value(_space->used());
}
inline void update_all() {
virtual inline void update_all() {
update_used();
update_capacity();
}

View file

@ -1,5 +1,5 @@
/*
* Copyright (c) 2001, 2011, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2001, 2012, 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
@ -175,7 +175,7 @@ SurrogateLockerThread::SurrogateLockerThread() :
{}
SurrogateLockerThread* SurrogateLockerThread::make(TRAPS) {
klassOop k =
Klass* k =
SystemDictionary::resolve_or_fail(vmSymbols::java_lang_Thread(),
true, CHECK_NULL);
instanceKlassHandle klass (THREAD, k);

View file

@ -40,7 +40,7 @@ void ImmutableSpace::initialize(MemRegion mr) {
_end = end;
}
void ImmutableSpace::oop_iterate(OopClosure* cl) {
void ImmutableSpace::oop_iterate(ExtendedOopClosure* cl) {
HeapWord* obj_addr = bottom();
HeapWord* t = end();
// Could call objects iterate, but this is easier.

View file

@ -59,7 +59,7 @@ class ImmutableSpace: public CHeapObj<mtGC> {
virtual size_t capacity_in_words(Thread*) const { return capacity_in_words(); }
// Iteration.
virtual void oop_iterate(OopClosure* cl);
virtual void oop_iterate(ExtendedOopClosure* cl);
virtual void object_iterate(ObjectClosure* cl);
// Debugging

View file

@ -1,5 +1,5 @@
/*
* Copyright (c) 1997, 2011, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 1997, 2012, 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
@ -26,13 +26,13 @@
#include "compiler/compileBroker.hpp"
#include "gc_implementation/shared/markSweep.inline.hpp"
#include "gc_interface/collectedHeap.inline.hpp"
#include "oops/methodDataOop.hpp"
#include "oops/methodData.hpp"
#include "oops/objArrayKlass.inline.hpp"
#include "oops/oop.inline.hpp"
unsigned int MarkSweep::_total_invocations = 0;
Stack<oop, mtGC> MarkSweep::_marking_stack;
Stack<DataLayout*, mtGC> MarkSweep::_revisit_mdo_stack;
Stack<Klass*, mtGC> MarkSweep::_revisit_klass_stack;
Stack<ObjArrayTask, mtGC> MarkSweep::_objarray_stack;
Stack<oop, mtGC> MarkSweep::_preserved_oop_stack;
@ -62,47 +62,6 @@ GrowableArray<HeapWord*>* MarkSweep::_last_gc_live_oops_moved_to = NULL;
GrowableArray<size_t> * MarkSweep::_last_gc_live_oops_size = NULL;
#endif
void MarkSweep::revisit_weak_klass_link(Klass* k) {
_revisit_klass_stack.push(k);
}
void MarkSweep::follow_weak_klass_links() {
// All klasses on the revisit stack are marked at this point.
// Update and follow all subklass, sibling and implementor links.
if (PrintRevisitStats) {
gclog_or_tty->print_cr("#classes in system dictionary = %d",
SystemDictionary::number_of_classes());
gclog_or_tty->print_cr("Revisit klass stack size = " SIZE_FORMAT,
_revisit_klass_stack.size());
}
while (!_revisit_klass_stack.is_empty()) {
Klass* const k = _revisit_klass_stack.pop();
k->follow_weak_klass_links(&is_alive, &keep_alive);
}
follow_stack();
}
void MarkSweep::revisit_mdo(DataLayout* p) {
_revisit_mdo_stack.push(p);
}
void MarkSweep::follow_mdo_weak_refs() {
// All strongly reachable oops have been marked at this point;
// we can visit and clear any weak references from MDO's which
// we memoized during the strong marking phase.
assert(_marking_stack.is_empty(), "Marking stack should be empty");
if (PrintRevisitStats) {
gclog_or_tty->print_cr("#classes in system dictionary = %d",
SystemDictionary::number_of_classes());
gclog_or_tty->print_cr("Revisit MDO stack size = " SIZE_FORMAT,
_revisit_mdo_stack.size());
}
while (!_revisit_mdo_stack.is_empty()) {
_revisit_mdo_stack.pop()->follow_weak_refs(&is_alive);
}
follow_stack();
}
MarkSweep::FollowRootClosure MarkSweep::follow_root_closure;
CodeBlobToOopClosure MarkSweep::follow_code_root_closure(&MarkSweep::follow_root_closure, /*do_marking=*/ true);
@ -110,10 +69,46 @@ void MarkSweep::FollowRootClosure::do_oop(oop* p) { follow_root(p); }
void MarkSweep::FollowRootClosure::do_oop(narrowOop* p) { follow_root(p); }
MarkSweep::MarkAndPushClosure MarkSweep::mark_and_push_closure;
MarkSweep::FollowKlassClosure MarkSweep::follow_klass_closure;
MarkSweep::AdjustKlassClosure MarkSweep::adjust_klass_closure;
void MarkSweep::MarkAndPushClosure::do_oop(oop* p) { assert(*p == NULL || (*p)->is_oop(), ""); mark_and_push(p); }
void MarkSweep::MarkAndPushClosure::do_oop(oop* p) { mark_and_push(p); }
void MarkSweep::MarkAndPushClosure::do_oop(narrowOop* p) { mark_and_push(p); }
void MarkSweep::FollowKlassClosure::do_klass(Klass* klass) {
klass->oops_do(&MarkSweep::mark_and_push_closure);
}
void MarkSweep::AdjustKlassClosure::do_klass(Klass* klass) {
klass->oops_do(&MarkSweep::adjust_pointer_closure);
}
void MarkSweep::follow_klass(Klass* klass) {
ClassLoaderData* cld = klass->class_loader_data();
assert(cld->has_defined(klass), "inconsistency!");
// The actual processing of the klass is done when we
// traverse the list of Klasses in the class loader data.
MarkSweep::follow_class_loader(cld);
}
void MarkSweep::adjust_klass(Klass* klass) {
ClassLoaderData* cld = klass->class_loader_data();
assert(cld->has_defined(klass), "inconsistency!");
// The actual processing of the klass is done when we
// traverse the list of Klasses in the class loader data.
MarkSweep::adjust_class_loader(cld);
}
void MarkSweep::follow_class_loader(ClassLoaderData* cld) {
cld->oops_do(&MarkSweep::mark_and_push_closure, &MarkSweep::follow_klass_closure, true);
}
void MarkSweep::adjust_class_loader(ClassLoaderData* cld) {
cld->oops_do(&MarkSweep::adjust_root_pointer_closure, &MarkSweep::adjust_klass_closure, true);
}
void MarkSweep::follow_stack() {
do {
while (!_marking_stack.is_empty()) {
@ -124,7 +119,7 @@ void MarkSweep::follow_stack() {
// Process ObjArrays one at a time to avoid marking stack bloat.
if (!_objarray_stack.is_empty()) {
ObjArrayTask task = _objarray_stack.pop();
objArrayKlass* const k = (objArrayKlass*)task.obj()->blueprint();
objArrayKlass* const k = (objArrayKlass*)task.obj()->klass();
k->oop_follow_contents(task.obj(), task.index());
}
} while (!_marking_stack.is_empty() || !_objarray_stack.is_empty());
@ -237,7 +232,7 @@ void MarkSweep::track_interior_pointers(oop obj) {
_pointer_tracking = true;
AdjusterTracker checker;
obj->oop_iterate(&checker);
obj->oop_iterate_no_header(&checker);
}
}
@ -248,10 +243,10 @@ void MarkSweep::check_interior_pointers() {
}
}
void MarkSweep::reset_live_oop_tracking(bool at_perm) {
void MarkSweep::reset_live_oop_tracking() {
if (ValidateMarkSweep) {
guarantee((size_t)_live_oops->length() == _live_oops_index, "should be at end of live oops");
_live_oops_index = at_perm ? _live_oops_index_at_perm : 0;
_live_oops_index = 0;
}
}

View file

@ -1,5 +1,5 @@
/*
* Copyright (c) 1997, 2011, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 1997, 2012, 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
@ -76,8 +76,17 @@ class MarkSweep : AllStatic {
public:
virtual void do_oop(oop* p);
virtual void do_oop(narrowOop* p);
virtual const bool should_remember_mdo() const { return true; }
virtual void remember_mdo(DataLayout* p) { MarkSweep::revisit_mdo(p); }
};
// The one and only place to start following the classes.
// Should only be applied to the ClassLoaderData klasses list.
class FollowKlassClosure : public KlassClosure {
public:
void do_klass(Klass* klass);
};
class AdjustKlassClosure : public KlassClosure {
public:
void do_klass(Klass* klass);
};
class FollowStackClosure: public VoidClosure {
@ -121,13 +130,12 @@ class MarkSweep : AllStatic {
// Vars
//
protected:
// Total invocations of a MarkSweep collection
static unsigned int _total_invocations;
// Traversal stacks used during phase1
static Stack<oop, mtGC> _marking_stack;
static Stack<ObjArrayTask, mtGC> _objarray_stack;
// Stack for live klasses to revisit at end of marking phase
static Stack<Klass*, mtGC> _revisit_klass_stack;
// Set (stack) of MDO's to revisit at end of marking phase
static Stack<DataLayout*, mtGC> _revisit_mdo_stack;
// Space for storing/restoring mark word
static Stack<markOop, mtGC> _preserved_mark_stack;
@ -167,27 +175,25 @@ class MarkSweep : AllStatic {
#endif
// Non public closures
static IsAliveClosure is_alive;
static KeepAliveClosure keep_alive;
// Class unloading. Update subklass/sibling/implementor links at end of marking phase.
static void follow_weak_klass_links();
// Class unloading. Clear weak refs in MDO's (ProfileData)
// at the end of the marking phase.
static void follow_mdo_weak_refs();
// Debugging
static void trace(const char* msg) PRODUCT_RETURN;
public:
// Public closures
static IsAliveClosure is_alive;
static FollowRootClosure follow_root_closure;
static CodeBlobToOopClosure follow_code_root_closure; // => follow_root_closure
static MarkAndPushClosure mark_and_push_closure;
static FollowKlassClosure follow_klass_closure;
static FollowStackClosure follow_stack_closure;
static AdjustPointerClosure adjust_root_pointer_closure;
static AdjustPointerClosure adjust_pointer_closure;
static AdjustKlassClosure adjust_klass_closure;
// Accessors
static unsigned int total_invocations() { return _total_invocations; }
// Reference Processing
static ReferenceProcessor* const ref_processor() { return _ref_processor; }
@ -196,12 +202,20 @@ class MarkSweep : AllStatic {
static void mark_object(oop obj);
// Mark pointer and follow contents. Empty marking stack afterwards.
template <class T> static inline void follow_root(T* p);
// Check mark and maybe push on marking stack
template <class T> static inline void mark_and_push(T* p);
template <class T> static void mark_and_push(T* p);
static inline void push_objarray(oop obj, size_t index);
static void follow_stack(); // Empty marking stack.
static void follow_klass(Klass* klass);
static void adjust_klass(Klass* klass);
static void follow_class_loader(ClassLoaderData* cld);
static void adjust_class_loader(ClassLoaderData* cld);
static void preserve_mark(oop p, markOop mark);
// Save the mark word so it can be restored later
static void adjust_marks(); // Adjust the pointers in the preserved marks table
@ -219,7 +233,7 @@ class MarkSweep : AllStatic {
static void track_interior_pointers(oop obj);
static void check_interior_pointers();
static void reset_live_oop_tracking(bool at_perm);
static void reset_live_oop_tracking();
static void register_live_oop(oop p, size_t size);
static void validate_live_oop(oop p, size_t size);
static void live_oop_moved_to(HeapWord* q, size_t size, HeapWord* compaction_top);
@ -231,12 +245,6 @@ class MarkSweep : AllStatic {
// tracking down heap stomps.
static void print_new_location_of_heap_address(HeapWord* q);
#endif
// Call backs for class unloading
// Update subklass/sibling/implementor links at end of marking.
static void revisit_weak_klass_link(Klass* k);
// For weak refs clearing in MDO's
static void revisit_mdo(DataLayout* p);
};
class PreservedMark VALUE_OBJ_CLASS_SPEC {

View file

@ -1,5 +1,5 @@
/*
* Copyright (c) 2000, 2011, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2000, 2012, 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
@ -88,9 +88,8 @@ template <class T> inline void MarkSweep::adjust_pointer(T* p, bool isroot) {
oop new_obj = oop(obj->mark()->decode_pointer());
assert(new_obj != NULL || // is forwarding ptr?
obj->mark() == markOopDesc::prototype() || // not gc marked?
(UseBiasedLocking && obj->mark()->has_bias_pattern()) ||
(UseBiasedLocking && obj->mark()->has_bias_pattern()),
// not gc marked?
obj->is_shared(), // never forwarded?
"should be forwarded");
if (new_obj != NULL) {
assert(Universe::heap()->is_in_reserved(new_obj),

View file

@ -216,7 +216,7 @@ bool MutableSpace::cas_deallocate(HeapWord *obj, size_t size) {
return (HeapWord*)Atomic::cmpxchg_ptr(obj, top_addr(), expected_top) == expected_top;
}
void MutableSpace::oop_iterate(OopClosure* cl) {
void MutableSpace::oop_iterate(ExtendedOopClosure* cl) {
HeapWord* obj_addr = bottom();
HeapWord* t = top();
// Could call objects iterate, but this is easier.
@ -225,6 +225,15 @@ void MutableSpace::oop_iterate(OopClosure* cl) {
}
}
void MutableSpace::oop_iterate_no_header(OopClosure* cl) {
HeapWord* obj_addr = bottom();
HeapWord* t = top();
// Could call objects iterate, but this is easier.
while (obj_addr < t) {
obj_addr += oop(obj_addr)->oop_iterate_no_header(cl);
}
}
void MutableSpace::object_iterate(ObjectClosure* cl) {
HeapWord* p = bottom();
while (p < top()) {

View file

@ -133,7 +133,8 @@ class MutableSpace: public ImmutableSpace {
bool cas_deallocate(HeapWord *obj, size_t size);
// Iteration.
void oop_iterate(OopClosure* cl);
void oop_iterate(ExtendedOopClosure* cl);
void oop_iterate_no_header(OopClosure* cl);
void object_iterate(ObjectClosure* cl);
// Debugging

View file

@ -1,5 +1,5 @@
/*
* Copyright (c) 2005, 2011, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2005, 2012, 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
@ -192,31 +192,69 @@ void VM_GenCollectFull::doit() {
gch->do_full_collection(gch->must_clear_all_soft_refs(), _max_level);
}
void VM_GenCollectForPermanentAllocation::doit() {
void VM_CollectForMetadataAllocation::doit() {
SvcGCMarker sgcm(SvcGCMarker::FULL);
SharedHeap* heap = (SharedHeap*)Universe::heap();
CollectedHeap* heap = Universe::heap();
GCCauseSetter gccs(heap, _gc_cause);
switch (heap->kind()) {
case (CollectedHeap::GenCollectedHeap): {
GenCollectedHeap* gch = (GenCollectedHeap*)heap;
gch->do_full_collection(gch->must_clear_all_soft_refs(),
gch->n_gens() - 1);
break;
bool do_cms_concurrent = false;
// Check again if the space is available. Another thread
// may have similarly failed a metadata allocation and induced
// a GC that freed space for the allocation.
if (!MetadataAllocationFailALot) {
_result = _loader_data->metaspace_non_null()->allocate(_size, _mdtype);
}
#ifndef SERIALGC
case (CollectedHeap::G1CollectedHeap): {
G1CollectedHeap* g1h = (G1CollectedHeap*)heap;
g1h->do_full_collection(_gc_cause == GCCause::_last_ditch_collection);
break;
if (_result == NULL) {
if (!UseConcMarkSweepGC) {
// Don't clear the soft refs the first time.
heap->collect_as_vm_thread(GCCause::_metadata_GC_threshold);
_result = _loader_data->metaspace_non_null()->allocate(_size, _mdtype);
// Don't do this for now
// This seems too costly to do a second full GC
// Let the metaspace grow instead
// if (_result == NULL) {
// // If allocation fails again, clear soft refs
// heap->collect_as_vm_thread(GCCause::_last_ditch_collection);
// _result = _loader_data->metaspace_non_null()->allocate(_size, _mdtype);
// }
} else {
MetaspaceGC::set_should_concurrent_collect(true);
do_cms_concurrent = true;
}
#endif // SERIALGC
default:
ShouldNotReachHere();
if (_result == NULL) {
// If still failing, allow the Metaspace to expand.
// See delta_capacity_until_GC() for explanation of the
// amount of the expansion.
// This should work unless there really is no more space
// or a MaxMetaspaceSize has been specified on the command line.
MetaspaceGC::set_expand_after_GC(true);
size_t before_inc = MetaspaceGC::capacity_until_GC();
size_t delta_words = MetaspaceGC::delta_capacity_until_GC(_size);
MetaspaceGC::inc_capacity_until_GC(delta_words);
if (PrintGCDetails && Verbose) {
gclog_or_tty->print_cr("Increase capacity to GC from " SIZE_FORMAT
" to " SIZE_FORMAT, before_inc, MetaspaceGC::capacity_until_GC());
}
_res = heap->perm_gen()->allocate(_size, false);
assert(heap->is_in_reserved_or_null(_res), "result not in heap");
if (_res == NULL && GC_locker::is_active_and_needs_gc()) {
_result = _loader_data->metaspace_non_null()->allocate(_size, _mdtype);
if (do_cms_concurrent && _result == NULL) {
// Rather than fail with a metaspace out-of-memory, do a full
// GC for CMS.
heap->collect_as_vm_thread(GCCause::_metadata_GC_threshold);
_result = _loader_data->metaspace_non_null()->allocate(_size, _mdtype);
}
if (_result == NULL) {
if (PrintGCDetails) {
gclog_or_tty->print_cr("\nAfter Metaspace GC failed to allocate size "
SIZE_FORMAT, _size);
}
}
}
}
if (_result == NULL && GC_locker::is_active_and_needs_gc()) {
set_gc_locked();
}
}

View file

@ -1,5 +1,5 @@
/*
* Copyright (c) 2005, 2011, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2005, 2012, 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
@ -42,7 +42,6 @@
// VM_GenCollectFull
// VM_GenCollectFullConcurrent
// VM_ParallelGCFailedAllocation
// VM_ParallelGCFailedPermanentAllocation
// VM_ParallelGCSystemGC
// VM_GC_Operation
// - implements methods common to all classes in the hierarchy:
@ -53,9 +52,7 @@
// is specified; and also the attach "inspectheap" operation
//
// VM_GenCollectForAllocation
// VM_GenCollectForPermanentAllocation
// VM_ParallelGCFailedAllocation
// VM_ParallelGCFailedPermanentAllocation
// - this operation is invoked when allocation is failed;
// operation performs garbage collection and tries to
// allocate afterwards;
@ -191,24 +188,27 @@ class VM_GenCollectFull: public VM_GC_Operation {
virtual void doit();
};
class VM_GenCollectForPermanentAllocation: public VM_GC_Operation {
class VM_CollectForMetadataAllocation: public VM_GC_Operation {
private:
HeapWord* _res;
MetaWord* _result;
size_t _size; // size of object to be allocated
Metaspace::MetadataType _mdtype;
ClassLoaderData* _loader_data;
public:
VM_GenCollectForPermanentAllocation(size_t size,
VM_CollectForMetadataAllocation(ClassLoaderData* loader_data,
size_t size, Metaspace::MetadataType mdtype,
unsigned int gc_count_before,
unsigned int full_gc_count_before,
GCCause::Cause gc_cause)
: VM_GC_Operation(gc_count_before, gc_cause, full_gc_count_before, true),
_size(size) {
_res = NULL;
_gc_cause = gc_cause;
_loader_data(loader_data), _size(size), _mdtype(mdtype), _result(NULL) {
}
~VM_GenCollectForPermanentAllocation() {}
virtual VMOp_Type type() const { return VMOp_GenCollectForPermanentAllocation; }
~VM_CollectForMetadataAllocation() {
MetaspaceGC::set_expand_after_GC(false);
}
virtual VMOp_Type type() const { return VMOp_CollectForMetadataAllocation; }
virtual void doit();
HeapWord* result() const { return _res; }
MetaWord* result() const { return _result; }
};
class SvcGCMarker : public StackObj {