You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
362 lines
15 KiB
362 lines
15 KiB
/*
|
|
* Copyright (C) 2015 The Android Open Source Project
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
|
|
#include "space_test.h"
|
|
|
|
#include "dlmalloc_space.h"
|
|
#include "rosalloc_space.h"
|
|
#include "scoped_thread_state_change-inl.h"
|
|
|
|
namespace art {
|
|
namespace gc {
|
|
namespace space {
|
|
|
|
enum MallocSpaceType {
|
|
kMallocSpaceDlMalloc,
|
|
kMallocSpaceRosAlloc,
|
|
};
|
|
|
|
class SpaceCreateTest : public SpaceTest<CommonRuntimeTestWithParam<MallocSpaceType>> {
|
|
public:
|
|
MallocSpace* CreateSpace(const std::string& name,
|
|
size_t initial_size,
|
|
size_t growth_limit,
|
|
size_t capacity) {
|
|
const MallocSpaceType type = GetParam();
|
|
if (type == kMallocSpaceDlMalloc) {
|
|
return DlMallocSpace::Create(name,
|
|
initial_size,
|
|
growth_limit,
|
|
capacity,
|
|
/*can_move_objects=*/ false);
|
|
}
|
|
DCHECK_EQ(static_cast<uint32_t>(type), static_cast<uint32_t>(kMallocSpaceRosAlloc));
|
|
return RosAllocSpace::Create(name,
|
|
initial_size,
|
|
growth_limit,
|
|
capacity,
|
|
Runtime::Current()->GetHeap()->IsLowMemoryMode(),
|
|
/*can_move_objects=*/ false);
|
|
}
|
|
};
|
|
|
|
TEST_P(SpaceCreateTest, InitTestBody) {
|
|
// This will lead to error messages in the log.
|
|
ScopedLogSeverity sls(LogSeverity::FATAL);
|
|
|
|
{
|
|
// Init < max == growth
|
|
std::unique_ptr<Space> space(CreateSpace("test", 16 * MB, 32 * MB, 32 * MB));
|
|
EXPECT_TRUE(space != nullptr);
|
|
// Init == max == growth
|
|
space.reset(CreateSpace("test", 16 * MB, 16 * MB, 16 * MB));
|
|
EXPECT_TRUE(space != nullptr);
|
|
// Init > max == growth
|
|
space.reset(CreateSpace("test", 32 * MB, 16 * MB, 16 * MB));
|
|
EXPECT_TRUE(space == nullptr);
|
|
// Growth == init < max
|
|
space.reset(CreateSpace("test", 16 * MB, 16 * MB, 32 * MB));
|
|
EXPECT_TRUE(space != nullptr);
|
|
// Growth < init < max
|
|
space.reset(CreateSpace("test", 16 * MB, 8 * MB, 32 * MB));
|
|
EXPECT_TRUE(space == nullptr);
|
|
// Init < growth < max
|
|
space.reset(CreateSpace("test", 8 * MB, 16 * MB, 32 * MB));
|
|
EXPECT_TRUE(space != nullptr);
|
|
// Init < max < growth
|
|
space.reset(CreateSpace("test", 8 * MB, 32 * MB, 16 * MB));
|
|
EXPECT_TRUE(space == nullptr);
|
|
}
|
|
}
|
|
|
|
// TODO: This test is not very good, we should improve it.
|
|
// The test should do more allocations before the creation of the ZygoteSpace, and then do
|
|
// allocations after the ZygoteSpace is created. The test should also do some GCs to ensure that
|
|
// the GC works with the ZygoteSpace.
|
|
TEST_P(SpaceCreateTest, ZygoteSpaceTestBody) {
|
|
size_t unused;
|
|
MallocSpace* space(CreateSpace("test", 4 * MB, 16 * MB, 16 * MB));
|
|
ASSERT_TRUE(space != nullptr);
|
|
|
|
// Make space findable to the heap, will also delete space when runtime is cleaned up
|
|
AddSpace(space);
|
|
Thread* self = Thread::Current();
|
|
ScopedObjectAccess soa(self);
|
|
|
|
// Succeeds, fits without adjusting the footprint limit.
|
|
size_t ptr1_bytes_allocated, ptr1_usable_size, ptr1_bytes_tl_bulk_allocated;
|
|
StackHandleScope<3> hs(soa.Self());
|
|
MutableHandle<mirror::Object> ptr1(hs.NewHandle(Alloc(space,
|
|
self,
|
|
1 * MB,
|
|
&ptr1_bytes_allocated,
|
|
&ptr1_usable_size,
|
|
&ptr1_bytes_tl_bulk_allocated)));
|
|
EXPECT_TRUE(ptr1 != nullptr);
|
|
EXPECT_LE(1U * MB, ptr1_bytes_allocated);
|
|
EXPECT_LE(1U * MB, ptr1_usable_size);
|
|
EXPECT_LE(ptr1_usable_size, ptr1_bytes_allocated);
|
|
EXPECT_EQ(ptr1_bytes_tl_bulk_allocated, ptr1_bytes_allocated);
|
|
|
|
// Fails, requires a higher footprint limit.
|
|
mirror::Object* ptr2 = Alloc(space, self, 8 * MB, &unused, nullptr, &unused);
|
|
EXPECT_TRUE(ptr2 == nullptr);
|
|
|
|
// Succeeds, adjusts the footprint.
|
|
size_t ptr3_bytes_allocated, ptr3_usable_size, ptr3_bytes_tl_bulk_allocated;
|
|
MutableHandle<mirror::Object> ptr3(hs.NewHandle(AllocWithGrowth(space,
|
|
self,
|
|
8 * MB,
|
|
&ptr3_bytes_allocated,
|
|
&ptr3_usable_size,
|
|
&ptr3_bytes_tl_bulk_allocated)));
|
|
EXPECT_TRUE(ptr3 != nullptr);
|
|
EXPECT_LE(8U * MB, ptr3_bytes_allocated);
|
|
EXPECT_LE(8U * MB, ptr3_usable_size);
|
|
EXPECT_LE(ptr3_usable_size, ptr3_bytes_allocated);
|
|
EXPECT_EQ(ptr3_bytes_tl_bulk_allocated, ptr3_bytes_allocated);
|
|
|
|
// Fails, requires a higher footprint limit.
|
|
mirror::Object* ptr4 = space->Alloc(self, 8 * MB, &unused, nullptr, &unused);
|
|
EXPECT_TRUE(ptr4 == nullptr);
|
|
|
|
// Also fails, requires a higher allowed footprint.
|
|
mirror::Object* ptr5 = space->AllocWithGrowth(self, 8 * MB, &unused, nullptr, &unused);
|
|
EXPECT_TRUE(ptr5 == nullptr);
|
|
|
|
// Release some memory.
|
|
size_t free3 = space->AllocationSize(ptr3.Get(), nullptr);
|
|
EXPECT_EQ(free3, ptr3_bytes_allocated);
|
|
EXPECT_EQ(free3, space->Free(self, ptr3.Assign(nullptr)));
|
|
EXPECT_LE(8U * MB, free3);
|
|
|
|
// Succeeds, now that memory has been freed.
|
|
size_t ptr6_bytes_allocated, ptr6_usable_size, ptr6_bytes_tl_bulk_allocated;
|
|
Handle<mirror::Object> ptr6(hs.NewHandle(AllocWithGrowth(space,
|
|
self,
|
|
9 * MB,
|
|
&ptr6_bytes_allocated,
|
|
&ptr6_usable_size,
|
|
&ptr6_bytes_tl_bulk_allocated)));
|
|
EXPECT_TRUE(ptr6 != nullptr);
|
|
EXPECT_LE(9U * MB, ptr6_bytes_allocated);
|
|
EXPECT_LE(9U * MB, ptr6_usable_size);
|
|
EXPECT_LE(ptr6_usable_size, ptr6_bytes_allocated);
|
|
EXPECT_EQ(ptr6_bytes_tl_bulk_allocated, ptr6_bytes_allocated);
|
|
|
|
// Final clean up.
|
|
size_t free1 = space->AllocationSize(ptr1.Get(), nullptr);
|
|
space->Free(self, ptr1.Assign(nullptr));
|
|
EXPECT_LE(1U * MB, free1);
|
|
|
|
// Make sure that the zygote space isn't directly at the start of the space.
|
|
EXPECT_TRUE(space->Alloc(self, 1U * MB, &unused, nullptr, &unused) != nullptr);
|
|
|
|
gc::Heap* heap = Runtime::Current()->GetHeap();
|
|
space::Space* old_space = space;
|
|
{
|
|
ScopedThreadSuspension sts(self, kSuspended);
|
|
ScopedSuspendAll ssa("Add image space");
|
|
heap->RemoveSpace(old_space);
|
|
}
|
|
heap->RevokeAllThreadLocalBuffers();
|
|
space::ZygoteSpace* zygote_space = space->CreateZygoteSpace("alloc space",
|
|
heap->IsLowMemoryMode(),
|
|
&space);
|
|
delete old_space;
|
|
// Add the zygote space.
|
|
AddSpace(zygote_space, false);
|
|
|
|
// Make space findable to the heap, will also delete space when runtime is cleaned up
|
|
AddSpace(space, false);
|
|
|
|
// Succeeds, fits without adjusting the footprint limit.
|
|
ptr1.Assign(Alloc(space,
|
|
self,
|
|
1 * MB,
|
|
&ptr1_bytes_allocated,
|
|
&ptr1_usable_size,
|
|
&ptr1_bytes_tl_bulk_allocated));
|
|
EXPECT_TRUE(ptr1 != nullptr);
|
|
EXPECT_LE(1U * MB, ptr1_bytes_allocated);
|
|
EXPECT_LE(1U * MB, ptr1_usable_size);
|
|
EXPECT_LE(ptr1_usable_size, ptr1_bytes_allocated);
|
|
EXPECT_EQ(ptr1_bytes_tl_bulk_allocated, ptr1_bytes_allocated);
|
|
|
|
// Fails, requires a higher footprint limit.
|
|
ptr2 = Alloc(space, self, 8 * MB, &unused, nullptr, &unused);
|
|
EXPECT_TRUE(ptr2 == nullptr);
|
|
|
|
// Succeeds, adjusts the footprint.
|
|
ptr3.Assign(AllocWithGrowth(space,
|
|
self,
|
|
2 * MB,
|
|
&ptr3_bytes_allocated,
|
|
&ptr3_usable_size,
|
|
&ptr3_bytes_tl_bulk_allocated));
|
|
EXPECT_TRUE(ptr3 != nullptr);
|
|
EXPECT_LE(2U * MB, ptr3_bytes_allocated);
|
|
EXPECT_LE(2U * MB, ptr3_usable_size);
|
|
EXPECT_LE(ptr3_usable_size, ptr3_bytes_allocated);
|
|
EXPECT_EQ(ptr3_bytes_tl_bulk_allocated, ptr3_bytes_allocated);
|
|
space->Free(self, ptr3.Assign(nullptr));
|
|
|
|
// Final clean up.
|
|
free1 = space->AllocationSize(ptr1.Get(), nullptr);
|
|
space->Free(self, ptr1.Assign(nullptr));
|
|
EXPECT_LE(1U * MB, free1);
|
|
}
|
|
|
|
TEST_P(SpaceCreateTest, AllocAndFreeTestBody) {
|
|
size_t unused = 0;
|
|
MallocSpace* space(CreateSpace("test", 4 * MB, 16 * MB, 16 * MB));
|
|
ASSERT_TRUE(space != nullptr);
|
|
Thread* self = Thread::Current();
|
|
ScopedObjectAccess soa(self);
|
|
|
|
// Make space findable to the heap, will also delete space when runtime is cleaned up
|
|
AddSpace(space);
|
|
|
|
// Succeeds, fits without adjusting the footprint limit.
|
|
size_t ptr1_bytes_allocated, ptr1_usable_size, ptr1_bytes_tl_bulk_allocated;
|
|
StackHandleScope<3> hs(soa.Self());
|
|
MutableHandle<mirror::Object> ptr1(hs.NewHandle(Alloc(space,
|
|
self,
|
|
1 * MB,
|
|
&ptr1_bytes_allocated,
|
|
&ptr1_usable_size,
|
|
&ptr1_bytes_tl_bulk_allocated)));
|
|
EXPECT_TRUE(ptr1 != nullptr);
|
|
EXPECT_LE(1U * MB, ptr1_bytes_allocated);
|
|
EXPECT_LE(1U * MB, ptr1_usable_size);
|
|
EXPECT_LE(ptr1_usable_size, ptr1_bytes_allocated);
|
|
EXPECT_EQ(ptr1_bytes_tl_bulk_allocated, ptr1_bytes_allocated);
|
|
|
|
// Fails, requires a higher footprint limit.
|
|
mirror::Object* ptr2 = Alloc(space, self, 8 * MB, &unused, nullptr, &unused);
|
|
EXPECT_TRUE(ptr2 == nullptr);
|
|
|
|
// Succeeds, adjusts the footprint.
|
|
size_t ptr3_bytes_allocated, ptr3_usable_size, ptr3_bytes_tl_bulk_allocated;
|
|
MutableHandle<mirror::Object> ptr3(hs.NewHandle(AllocWithGrowth(space,
|
|
self,
|
|
8 * MB,
|
|
&ptr3_bytes_allocated,
|
|
&ptr3_usable_size,
|
|
&ptr3_bytes_tl_bulk_allocated)));
|
|
EXPECT_TRUE(ptr3 != nullptr);
|
|
EXPECT_LE(8U * MB, ptr3_bytes_allocated);
|
|
EXPECT_LE(8U * MB, ptr3_usable_size);
|
|
EXPECT_LE(ptr3_usable_size, ptr3_bytes_allocated);
|
|
EXPECT_EQ(ptr3_bytes_tl_bulk_allocated, ptr3_bytes_allocated);
|
|
|
|
// Fails, requires a higher footprint limit.
|
|
mirror::Object* ptr4 = Alloc(space, self, 8 * MB, &unused, nullptr, &unused);
|
|
EXPECT_TRUE(ptr4 == nullptr);
|
|
|
|
// Also fails, requires a higher allowed footprint.
|
|
mirror::Object* ptr5 = AllocWithGrowth(space, self, 8 * MB, &unused, nullptr, &unused);
|
|
EXPECT_TRUE(ptr5 == nullptr);
|
|
|
|
// Release some memory.
|
|
size_t free3 = space->AllocationSize(ptr3.Get(), nullptr);
|
|
EXPECT_EQ(free3, ptr3_bytes_allocated);
|
|
space->Free(self, ptr3.Assign(nullptr));
|
|
EXPECT_LE(8U * MB, free3);
|
|
|
|
// Succeeds, now that memory has been freed.
|
|
size_t ptr6_bytes_allocated, ptr6_usable_size, ptr6_bytes_tl_bulk_allocated;
|
|
Handle<mirror::Object> ptr6(hs.NewHandle(AllocWithGrowth(space,
|
|
self,
|
|
9 * MB,
|
|
&ptr6_bytes_allocated,
|
|
&ptr6_usable_size,
|
|
&ptr6_bytes_tl_bulk_allocated)));
|
|
EXPECT_TRUE(ptr6 != nullptr);
|
|
EXPECT_LE(9U * MB, ptr6_bytes_allocated);
|
|
EXPECT_LE(9U * MB, ptr6_usable_size);
|
|
EXPECT_LE(ptr6_usable_size, ptr6_bytes_allocated);
|
|
EXPECT_EQ(ptr6_bytes_tl_bulk_allocated, ptr6_bytes_allocated);
|
|
|
|
// Final clean up.
|
|
size_t free1 = space->AllocationSize(ptr1.Get(), nullptr);
|
|
space->Free(self, ptr1.Assign(nullptr));
|
|
EXPECT_LE(1U * MB, free1);
|
|
}
|
|
|
|
TEST_P(SpaceCreateTest, AllocAndFreeListTestBody) {
|
|
MallocSpace* space(CreateSpace("test", 4 * MB, 16 * MB, 16 * MB));
|
|
ASSERT_TRUE(space != nullptr);
|
|
|
|
// Make space findable to the heap, will also delete space when runtime is cleaned up
|
|
AddSpace(space);
|
|
Thread* self = Thread::Current();
|
|
ScopedObjectAccess soa(self);
|
|
|
|
// Succeeds, fits without adjusting the max allowed footprint.
|
|
mirror::Object* lots_of_objects[1024];
|
|
for (size_t i = 0; i < arraysize(lots_of_objects); i++) {
|
|
size_t allocation_size, usable_size, bytes_tl_bulk_allocated;
|
|
size_t size_of_zero_length_byte_array = SizeOfZeroLengthByteArray();
|
|
lots_of_objects[i] = Alloc(space,
|
|
self,
|
|
size_of_zero_length_byte_array,
|
|
&allocation_size,
|
|
&usable_size,
|
|
&bytes_tl_bulk_allocated);
|
|
EXPECT_TRUE(lots_of_objects[i] != nullptr);
|
|
size_t computed_usable_size;
|
|
EXPECT_EQ(allocation_size, space->AllocationSize(lots_of_objects[i], &computed_usable_size));
|
|
EXPECT_EQ(usable_size, computed_usable_size);
|
|
EXPECT_TRUE(bytes_tl_bulk_allocated == 0 ||
|
|
bytes_tl_bulk_allocated >= allocation_size);
|
|
}
|
|
|
|
// Release memory.
|
|
space->FreeList(self, arraysize(lots_of_objects), lots_of_objects);
|
|
|
|
// Succeeds, fits by adjusting the max allowed footprint.
|
|
for (size_t i = 0; i < arraysize(lots_of_objects); i++) {
|
|
size_t allocation_size, usable_size, bytes_tl_bulk_allocated;
|
|
lots_of_objects[i] = AllocWithGrowth(space,
|
|
self,
|
|
1024,
|
|
&allocation_size,
|
|
&usable_size,
|
|
&bytes_tl_bulk_allocated);
|
|
EXPECT_TRUE(lots_of_objects[i] != nullptr);
|
|
size_t computed_usable_size;
|
|
EXPECT_EQ(allocation_size, space->AllocationSize(lots_of_objects[i], &computed_usable_size));
|
|
EXPECT_EQ(usable_size, computed_usable_size);
|
|
EXPECT_TRUE(bytes_tl_bulk_allocated == 0 ||
|
|
bytes_tl_bulk_allocated >= allocation_size);
|
|
}
|
|
|
|
// Release memory.
|
|
space->FreeList(self, arraysize(lots_of_objects), lots_of_objects);
|
|
}
|
|
|
|
INSTANTIATE_TEST_CASE_P(CreateRosAllocSpace,
|
|
SpaceCreateTest,
|
|
testing::Values(kMallocSpaceRosAlloc));
|
|
INSTANTIATE_TEST_CASE_P(CreateDlMallocSpace,
|
|
SpaceCreateTest,
|
|
testing::Values(kMallocSpaceDlMalloc));
|
|
|
|
} // namespace space
|
|
} // namespace gc
|
|
} // namespace art
|