Reduce IndirectReferenceTable memory usage
Changed the slot side table and the main table to be a single
table, reduced number of slots per reference from 4 to 3 to make
the IrtEntry a power of 2 size.
Before:
20848 kB: Dalvik Other
After:
16760 kB: Dalvik Other
Bug: 17643507
Change-Id: I4820ca381e906c2e396208df67f2f53816047bf5
diff --git a/runtime/indirect_reference_table.h b/runtime/indirect_reference_table.h
index fb910e2..5a178ea 100644
--- a/runtime/indirect_reference_table.h
+++ b/runtime/indirect_reference_table.h
@@ -127,16 +127,6 @@
return static_cast<IndirectRefKind>(reinterpret_cast<uintptr_t>(iref) & 0x03);
}
-/*
- * Extended debugging structure. We keep a parallel array of these, one
- * per slot in the table.
- */
-static const size_t kIRTPrevCount = 4;
-struct IndirectRefSlot {
- uint32_t serial;
- const mirror::Object* previous[kIRTPrevCount];
-};
-
/* use as initial value for "cookie", and when table has only one segment */
static const uint32_t IRT_FIRST_SEGMENT = 0;
@@ -203,9 +193,35 @@
} parts;
};
+// Try to choose kIRTPrevCount so that sizeof(IrtEntry) is a power of 2.
+// Contains multiple entries but only one active one, this helps us detect use after free errors
+// since the serial stored in the indirect ref wont match.
+static const size_t kIRTPrevCount = kIsDebugBuild ? 7 : 3;
+class PACKED(4) IrtEntry {
+ public:
+ void Add(mirror::Object* obj) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+ ++serial_;
+ if (serial_ == kIRTPrevCount) {
+ serial_ = 0;
+ }
+ references_[serial_] = GcRoot<mirror::Object>(obj);
+ }
+ GcRoot<mirror::Object>* GetReference() {
+ DCHECK_LT(serial_, kIRTPrevCount);
+ return &references_[serial_];
+ }
+ uint32_t GetSerial() const {
+ return serial_;
+ }
+
+ private:
+ uint32_t serial_;
+ GcRoot<mirror::Object> references_[kIRTPrevCount];
+};
+
class IrtIterator {
public:
- explicit IrtIterator(GcRoot<mirror::Object>* table, size_t i, size_t capacity)
+ explicit IrtIterator(IrtEntry* table, size_t i, size_t capacity)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_)
: table_(table), i_(i), capacity_(capacity) {
SkipNullsAndTombstones();
@@ -219,7 +235,7 @@
mirror::Object** operator*() {
// This does not have a read barrier as this is used to visit roots.
- return table_[i_].AddressWithoutBarrier();
+ return table_[i_].GetReference()->AddressWithoutBarrier();
}
bool equals(const IrtIterator& rhs) const {
@@ -230,13 +246,13 @@
void SkipNullsAndTombstones() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
// We skip NULLs and tombstones. Clients don't want to see implementation details.
while (i_ < capacity_ &&
- (table_[i_].IsNull() ||
- table_[i_].Read<kWithoutReadBarrier>() == kClearedJniWeakGlobal)) {
+ (table_[i_].GetReference()->IsNull() ||
+ table_[i_].GetReference()->Read<kWithoutReadBarrier>() == kClearedJniWeakGlobal)) {
++i_;
}
}
- GcRoot<mirror::Object>* const table_;
+ IrtEntry* const table_;
size_t i_;
size_t capacity_;
};
@@ -329,9 +345,7 @@
}
private:
- /*
- * Extract the table index from an indirect reference.
- */
+ // Extract the table index from an indirect reference.
static uint32_t ExtractIndex(IndirectRef iref) {
uintptr_t uref = reinterpret_cast<uintptr_t>(iref);
return (uref >> 2) & 0xffff;
@@ -343,25 +357,11 @@
*/
IndirectRef ToIndirectRef(uint32_t tableIndex) const {
DCHECK_LT(tableIndex, 65536U);
- uint32_t serialChunk = slot_data_[tableIndex].serial;
- uintptr_t uref = serialChunk << 20 | (tableIndex << 2) | kind_;
+ uint32_t serialChunk = table_[tableIndex].GetSerial();
+ uintptr_t uref = (serialChunk << 20) | (tableIndex << 2) | kind_;
return reinterpret_cast<IndirectRef>(uref);
}
- /*
- * Update extended debug info when an entry is added.
- *
- * We advance the serial number, invalidating any outstanding references to
- * this slot.
- */
- void UpdateSlotAdd(const mirror::Object* obj, int slot) {
- if (slot_data_ != NULL) {
- IndirectRefSlot* pSlot = &slot_data_[slot];
- pSlot->serial++;
- pSlot->previous[pSlot->serial % kIRTPrevCount] = obj;
- }
- }
-
// Abort if check_jni is not enabled.
static void AbortIfNoCheckJNI();
@@ -374,19 +374,13 @@
// Mem map where we store the indirect refs.
std::unique_ptr<MemMap> table_mem_map_;
- // Mem map where we store the extended debugging info.
- std::unique_ptr<MemMap> slot_mem_map_;
// bottom of the stack. Do not directly access the object references
// in this as they are roots. Use Get() that has a read barrier.
- GcRoot<mirror::Object>* table_;
+ IrtEntry* table_;
/* bit mask, ORed into all irefs */
- IndirectRefKind kind_;
- /* extended debugging info */
- IndirectRefSlot* slot_data_;
- /* #of entries we have space for */
- size_t alloc_entries_;
+ const IndirectRefKind kind_;
/* max #of entries allowed */
- size_t max_entries_;
+ const size_t max_entries_;
};
} // namespace art