Make allocations report usable size.
Work-in-progress to allow arrays to fill usable size. Bug: 13028925.
Use C++11's override keyword on GCC >= 2.7 to ensure that we override GC and
allocator methods.
Move initial mirror::Class set up into a Functor so that all allocated objects
have non-zero sizes. Use this property to assert that all objects are never
larger than their usable size.
Other bits of GC related clean-up, missing initialization, missing use of
const, hot methods in .cc files, "unimplemented" functions that fail at
runtime in header files, reducing header file includes, move valgrind's space
into its own files, reduce number of array allocation routines.
Change-Id: Id5760041a2d7f94dcaf17ec760f6095ec75dadaa
diff --git a/runtime/mirror/array-inl.h b/runtime/mirror/array-inl.h
index 90aaccd..d44f75f 100644
--- a/runtime/mirror/array-inl.h
+++ b/runtime/mirror/array-inl.h
@@ -27,6 +27,10 @@
namespace art {
namespace mirror {
+static inline size_t HeaderSize(size_t component_size) {
+ return sizeof(Object) + (component_size == sizeof(int64_t) ? 8 : 4);
+}
+
template<VerifyObjectFlags kVerifyFlags>
inline size_t Array::SizeOf() {
// This is safe from overflow because the array was already allocated, so we know it's sane.
@@ -34,7 +38,7 @@
// Don't need to check this since we already check this in GetClass.
int32_t component_count =
GetLength<static_cast<VerifyObjectFlags>(kVerifyFlags & ~kVerifyThis)>();
- size_t header_size = sizeof(Object) + (component_size == sizeof(int64_t) ? 8 : 4);
+ size_t header_size = HeaderSize(component_size);
size_t data_size = component_count * component_size;
return header_size + data_size;
}
@@ -46,7 +50,7 @@
DCHECK_GE(component_count, 0);
DCHECK(array_class->IsArrayClass());
- size_t header_size = sizeof(Object) + (component_size == sizeof(int64_t) ? 8 : 4);
+ size_t header_size = HeaderSize(component_size);
size_t data_size = component_count * component_size;
size_t size = header_size + data_size;
@@ -61,13 +65,16 @@
return size;
}
-// Used for setting the array length in the allocation code path to ensure it is guarded by a CAS.
+// Used for setting the array length in the allocation code path to ensure it is guarded by a
+// StoreStore fence.
class SetLengthVisitor {
public:
explicit SetLengthVisitor(int32_t length) : length_(length) {
}
- void operator()(Object* obj) const SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+ void operator()(Object* obj, size_t usable_size) const
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+ UNUSED(usable_size);
// Avoid AsArray as object is not yet in live bitmap or allocation stack.
Array* array = down_cast<Array*>(obj);
// DCHECK(array->IsArrayInstance());
@@ -76,41 +83,64 @@
private:
const int32_t length_;
+
+ DISALLOW_COPY_AND_ASSIGN(SetLengthVisitor);
+};
+
+// Similar to SetLengthVisitor, used for setting the array length to fill the usable size of an
+// array.
+class SetLengthToUsableSizeVisitor {
+ public:
+ SetLengthToUsableSizeVisitor(size_t header_size, size_t component_size) :
+ header_size_(header_size), component_size_(component_size) {
+ }
+
+ void operator()(Object* obj, size_t usable_size) const
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+ // Avoid AsArray as object is not yet in live bitmap or allocation stack.
+ Array* array = down_cast<Array*>(obj);
+ uint32_t length = (usable_size - header_size_) / component_size_;
+ // DCHECK(array->IsArrayInstance());
+ array->SetLength(length);
+ }
+
+ private:
+ const size_t header_size_;
+ const size_t component_size_;
+
+ DISALLOW_COPY_AND_ASSIGN(SetLengthToUsableSizeVisitor);
};
template <bool kIsInstrumented>
inline Array* Array::Alloc(Thread* self, Class* array_class, int32_t component_count,
- size_t component_size, gc::AllocatorType allocator_type) {
+ size_t component_size, gc::AllocatorType allocator_type,
+ bool fill_usable) {
+ DCHECK(allocator_type != gc::kAllocatorTypeLOS);
size_t size = ComputeArraySize(self, array_class, component_count, component_size);
if (UNLIKELY(size == 0)) {
return nullptr;
}
gc::Heap* heap = Runtime::Current()->GetHeap();
- SetLengthVisitor visitor(component_count);
- DCHECK(allocator_type != gc::kAllocatorTypeLOS);
- return down_cast<Array*>(
- heap->AllocObjectWithAllocator<kIsInstrumented, true>(self, array_class, size,
- allocator_type, visitor));
-}
-
-template <bool kIsInstrumented>
-inline Array* Array::Alloc(Thread* self, Class* array_class, int32_t component_count,
- gc::AllocatorType allocator_type) {
- DCHECK(array_class->IsArrayClass());
- return Alloc<kIsInstrumented>(self, array_class, component_count, array_class->GetComponentSize(),
- allocator_type);
-}
-template <bool kIsInstrumented>
-inline Array* Array::Alloc(Thread* self, Class* array_class, int32_t component_count) {
- return Alloc<kIsInstrumented>(self, array_class, component_count,
- Runtime::Current()->GetHeap()->GetCurrentAllocator());
-}
-
-template <bool kIsInstrumented>
-inline Array* Array::Alloc(Thread* self, Class* array_class, int32_t component_count,
- size_t component_size) {
- return Alloc<kIsInstrumented>(self, array_class, component_count, component_size,
- Runtime::Current()->GetHeap()->GetCurrentAllocator());
+ Array* result;
+ if (!fill_usable) {
+ SetLengthVisitor visitor(component_count);
+ result = down_cast<Array*>(
+ heap->AllocObjectWithAllocator<kIsInstrumented, true>(self, array_class, size,
+ allocator_type, visitor));
+ } else {
+ SetLengthToUsableSizeVisitor visitor(HeaderSize(component_size), component_size);
+ result = down_cast<Array*>(
+ heap->AllocObjectWithAllocator<kIsInstrumented, true>(self, array_class, size,
+ allocator_type, visitor));
+ }
+ if (kIsDebugBuild && result != nullptr && Runtime::Current()->IsStarted()) {
+ if (!fill_usable) {
+ CHECK_EQ(result->SizeOf(), size);
+ } else {
+ CHECK_GE(result->SizeOf(), size);
+ }
+ }
+ return result;
}
template<class T>
@@ -133,9 +163,17 @@
}
}
+template<typename T>
+inline PrimitiveArray<T>* PrimitiveArray<T>::Alloc(Thread* self, size_t length) {
+ DCHECK(array_class_ != NULL);
+ Array* raw_array = Array::Alloc<true>(self, array_class_, length, sizeof(T),
+ Runtime::Current()->GetHeap()->GetCurrentAllocator());
+ return down_cast<PrimitiveArray<T>*>(raw_array);
+}
+
template<class T>
-void PrimitiveArray<T>::Memmove(int32_t dst_pos, PrimitiveArray<T>* src, int32_t src_pos,
- int32_t count) {
+inline void PrimitiveArray<T>::Memmove(int32_t dst_pos, PrimitiveArray<T>* src, int32_t src_pos,
+ int32_t count) {
if (UNLIKELY(count == 0)) {
return;
}
@@ -192,8 +230,8 @@
template<class T>
-void PrimitiveArray<T>::Memcpy(int32_t dst_pos, PrimitiveArray<T>* src, int32_t src_pos,
- int32_t count) {
+inline void PrimitiveArray<T>::Memcpy(int32_t dst_pos, PrimitiveArray<T>* src, int32_t src_pos,
+ int32_t count) {
if (UNLIKELY(count == 0)) {
return;
}
diff --git a/runtime/mirror/array.cc b/runtime/mirror/array.cc
index 715f072..139e2d0 100644
--- a/runtime/mirror/array.cc
+++ b/runtime/mirror/array.cc
@@ -46,7 +46,9 @@
const SirtRef<mirror::IntArray>& dimensions)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
int32_t array_length = dimensions->Get(current_dimension);
- SirtRef<Array> new_array(self, Array::Alloc<true>(self, array_class.get(), array_length));
+ SirtRef<Array> new_array(self, Array::Alloc<true>(self, array_class.get(), array_length,
+ array_class->GetComponentSize(),
+ Runtime::Current()->GetHeap()->GetCurrentAllocator()));
if (UNLIKELY(new_array.get() == nullptr)) {
CHECK(self->IsExceptionPending());
return nullptr;
@@ -117,13 +119,6 @@
art::ThrowArrayStoreException(object->GetClass(), this->GetClass());
}
-template<typename T>
-PrimitiveArray<T>* PrimitiveArray<T>::Alloc(Thread* self, size_t length) {
- DCHECK(array_class_ != NULL);
- Array* raw_array = Array::Alloc<true>(self, array_class_, length, sizeof(T));
- return down_cast<PrimitiveArray<T>*>(raw_array);
-}
-
template <typename T> Class* PrimitiveArray<T>::array_class_ = NULL;
// Explicitly instantiate all the primitive array types.
diff --git a/runtime/mirror/array.h b/runtime/mirror/array.h
index c4f9a75..772d303 100644
--- a/runtime/mirror/array.h
+++ b/runtime/mirror/array.h
@@ -28,25 +28,13 @@
class MANAGED Array : public Object {
public:
- // A convenience for code that doesn't know the component size, and doesn't want to have to work
- // it out itself.
+ // Allocates an array with the given properties, if fill_usable is true the array will be of at
+ // least component_count size, however, if there's usable space at the end of the allocation the
+ // array will fill it.
template <bool kIsInstrumented>
static Array* Alloc(Thread* self, Class* array_class, int32_t component_count,
- gc::AllocatorType allocator_type)
- SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
-
- template <bool kIsInstrumented>
- static Array* Alloc(Thread* self, Class* array_class, int32_t component_count,
- size_t component_size, gc::AllocatorType allocator_type)
- SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
-
- template <bool kIsInstrumented>
- static Array* Alloc(Thread* self, Class* array_class, int32_t component_count)
- SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
-
- template <bool kIsInstrumented>
- static Array* Alloc(Thread* self, Class* array_class, int32_t component_count,
- size_t component_size)
+ size_t component_size, gc::AllocatorType allocator_type,
+ bool fill_usable = false)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
static Array* CreateMultiArray(Thread* self, const SirtRef<Class>& element_class,
diff --git a/runtime/mirror/object_test.cc b/runtime/mirror/object_test.cc
index 5dfd007..7d8da14 100644
--- a/runtime/mirror/object_test.cc
+++ b/runtime/mirror/object_test.cc
@@ -148,16 +148,52 @@
TEST_F(ObjectTest, AllocArray) {
ScopedObjectAccess soa(Thread::Current());
Class* c = class_linker_->FindSystemClass(soa.Self(), "[I");
- SirtRef<Array> a(soa.Self(), Array::Alloc<true>(soa.Self(), c, 1));
- ASSERT_TRUE(c == a->GetClass());
+ SirtRef<Array> a(soa.Self(), Array::Alloc<true>(soa.Self(), c, 1, c->GetComponentSize(),
+ Runtime::Current()->GetHeap()->GetCurrentAllocator()));
+ EXPECT_TRUE(c == a->GetClass());
+ EXPECT_EQ(1, a->GetLength());
c = class_linker_->FindSystemClass(soa.Self(), "[Ljava/lang/Object;");
- a.reset(Array::Alloc<true>(soa.Self(), c, 1));
- ASSERT_TRUE(c == a->GetClass());
+ a.reset(Array::Alloc<true>(soa.Self(), c, 1, c->GetComponentSize(),
+ Runtime::Current()->GetHeap()->GetCurrentAllocator()));
+ EXPECT_TRUE(c == a->GetClass());
+ EXPECT_EQ(1, a->GetLength());
c = class_linker_->FindSystemClass(soa.Self(), "[[Ljava/lang/Object;");
- a.reset(Array::Alloc<true>(soa.Self(), c, 1));
- ASSERT_TRUE(c == a->GetClass());
+ a.reset(Array::Alloc<true>(soa.Self(), c, 1, c->GetComponentSize(),
+ Runtime::Current()->GetHeap()->GetCurrentAllocator()));
+ EXPECT_TRUE(c == a->GetClass());
+ EXPECT_EQ(1, a->GetLength());
+}
+
+TEST_F(ObjectTest, AllocArray_FillUsable) {
+ ScopedObjectAccess soa(Thread::Current());
+ Class* c = class_linker_->FindSystemClass(soa.Self(), "[B");
+ SirtRef<Array> a(soa.Self(), Array::Alloc<true>(soa.Self(), c, 1, c->GetComponentSize(),
+ Runtime::Current()->GetHeap()->GetCurrentAllocator(),
+ true));
+ EXPECT_TRUE(c == a->GetClass());
+ EXPECT_LE(1, a->GetLength());
+
+ c = class_linker_->FindSystemClass(soa.Self(), "[I");
+ a.reset(Array::Alloc<true>(soa.Self(), c, 2, c->GetComponentSize(),
+ Runtime::Current()->GetHeap()->GetCurrentAllocator(),
+ true));
+ EXPECT_TRUE(c == a->GetClass());
+ EXPECT_LE(2, a->GetLength());
+
+ c = class_linker_->FindSystemClass(soa.Self(), "[Ljava/lang/Object;");
+ a.reset(Array::Alloc<true>(soa.Self(), c, 2, c->GetComponentSize(),
+ Runtime::Current()->GetHeap()->GetCurrentAllocator(),
+ true));
+ EXPECT_TRUE(c == a->GetClass());
+ EXPECT_LE(2, a->GetLength());
+
+ c = class_linker_->FindSystemClass(soa.Self(), "[[Ljava/lang/Object;");
+ a.reset(Array::Alloc<true>(soa.Self(), c, 2, c->GetComponentSize(),
+ Runtime::Current()->GetHeap()->GetCurrentAllocator(), true));
+ EXPECT_TRUE(c == a->GetClass());
+ EXPECT_LE(2, a->GetLength());
}
template<typename ArrayT>