/* * Copyright (C) 2012 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. */ #ifndef ART_RUNTIME_GC_SPACE_LARGE_OBJECT_SPACE_H_ #define ART_RUNTIME_GC_SPACE_LARGE_OBJECT_SPACE_H_ #include "gc/accounting/gc_allocator.h" #include "dlmalloc_space.h" #include "safe_map.h" #include "space.h" #include #include namespace art { namespace gc { namespace space { // Abstraction implemented by all large object spaces. class LargeObjectSpace : public DiscontinuousSpace, public AllocSpace { public: SpaceType GetType() const OVERRIDE { return kSpaceTypeLargeObjectSpace; } void SwapBitmaps(); void CopyLiveToMarked(); virtual void Walk(DlMallocSpace::WalkCallback, void* arg) = 0; virtual ~LargeObjectSpace() {} uint64_t GetBytesAllocated() OVERRIDE { return num_bytes_allocated_; } uint64_t GetObjectsAllocated() OVERRIDE { return num_objects_allocated_; } uint64_t GetTotalBytesAllocated() const { return total_bytes_allocated_; } uint64_t GetTotalObjectsAllocated() const { return total_objects_allocated_; } size_t FreeList(Thread* self, size_t num_ptrs, mirror::Object** ptrs) OVERRIDE; // LargeObjectSpaces don't have thread local state. void RevokeThreadLocalBuffers(art::Thread*) OVERRIDE { } void RevokeAllThreadLocalBuffers() OVERRIDE { } bool IsAllocSpace() const OVERRIDE { return true; } AllocSpace* AsAllocSpace() OVERRIDE { return this; } void Sweep(bool swap_bitmaps, size_t* out_freed_objects, size_t* out_freed_bytes); virtual bool CanMoveObjects() const OVERRIDE { return false; } // Current address at which the space begins, which may vary as the space is filled. byte* Begin() const { return begin_; } // Current address at which the space ends, which may vary as the space is filled. byte* End() const { return end_; } protected: explicit LargeObjectSpace(const std::string& name, byte* begin, byte* end); static void SweepCallback(size_t num_ptrs, mirror::Object** ptrs, void* arg); // Approximate number of bytes which have been allocated into the space. uint64_t num_bytes_allocated_; uint64_t num_objects_allocated_; uint64_t total_bytes_allocated_; uint64_t total_objects_allocated_; // Begin and end, may change as more large objects are allocated. byte* begin_; byte* end_; friend class Space; private: DISALLOW_COPY_AND_ASSIGN(LargeObjectSpace); }; // A discontinuous large object space implemented by individual mmap/munmap calls. class LargeObjectMapSpace : public LargeObjectSpace { public: // Creates a large object space. Allocations into the large object space use memory maps instead // of malloc. static LargeObjectMapSpace* Create(const std::string& name); // Return the storage space required by obj. size_t AllocationSize(mirror::Object* obj, size_t* usable_size); mirror::Object* Alloc(Thread* self, size_t num_bytes, size_t* bytes_allocated, size_t* usable_size); size_t Free(Thread* self, mirror::Object* ptr); void Walk(DlMallocSpace::WalkCallback, void* arg) OVERRIDE LOCKS_EXCLUDED(lock_); // TODO: disabling thread safety analysis as this may be called when we already hold lock_. bool Contains(const mirror::Object* obj) const NO_THREAD_SAFETY_ANALYSIS; protected: explicit LargeObjectMapSpace(const std::string& name); virtual ~LargeObjectMapSpace() {} // Used to ensure mutual exclusion when the allocation spaces data structures are being modified. mutable Mutex lock_ DEFAULT_MUTEX_ACQUIRED_AFTER; std::vector > large_objects_ GUARDED_BY(lock_); typedef SafeMap, accounting::GcAllocator > > MemMaps; MemMaps mem_maps_ GUARDED_BY(lock_); }; // A continuous large object space with a free-list to handle holes. class FreeListSpace FINAL : public LargeObjectSpace { public: virtual ~FreeListSpace(); static FreeListSpace* Create(const std::string& name, byte* requested_begin, size_t capacity); size_t AllocationSize(mirror::Object* obj, size_t* usable_size) OVERRIDE EXCLUSIVE_LOCKS_REQUIRED(lock_); mirror::Object* Alloc(Thread* self, size_t num_bytes, size_t* bytes_allocated, size_t* usable_size) OVERRIDE; size_t Free(Thread* self, mirror::Object* obj) OVERRIDE; bool Contains(const mirror::Object* obj) const OVERRIDE; void Walk(DlMallocSpace::WalkCallback callback, void* arg) OVERRIDE LOCKS_EXCLUDED(lock_); // Address at which the space begins. byte* Begin() const { return begin_; } // Address at which the space ends, which may vary as the space is filled. byte* End() const { return end_; } // Current size of space size_t Size() const { return End() - Begin(); } void Dump(std::ostream& os) const; protected: static const size_t kAlignment = kPageSize; class AllocationHeader { public: // Returns the allocation size, includes the header. size_t AllocationSize() const { return alloc_size_; } // Updates the allocation size in the header, the allocation size includes the header itself. void SetAllocationSize(size_t size) { DCHECK(IsAligned(size)); alloc_size_ = size; } bool IsFree() const { return AllocationSize() == 0; } // Returns the previous free allocation header by using the prev_free_ member to figure out // where it is. If prev free is 0 then we just return ourself. AllocationHeader* GetPrevFreeAllocationHeader() { return reinterpret_cast(reinterpret_cast(this) - prev_free_); } // Returns the address of the object associated with this allocation header. mirror::Object* GetObjectAddress() { return reinterpret_cast(reinterpret_cast(this) + sizeof(*this)); } // Returns the next allocation header after the object associated with this allocation header. AllocationHeader* GetNextAllocationHeader() { DCHECK_NE(alloc_size_, 0U); return reinterpret_cast(reinterpret_cast(this) + alloc_size_); } // Returns how many free bytes there is before the block. size_t GetPrevFree() const { return prev_free_; } // Update the size of the free block prior to the allocation. void SetPrevFree(size_t prev_free) { DCHECK(IsAligned(prev_free)); prev_free_ = prev_free; } // Finds and returns the next non free allocation header after ourself. // TODO: Optimize, currently O(n) for n free following pages. AllocationHeader* GetNextNonFree(); // Used to implement best fit object allocation. Each allocation has an AllocationHeader which // contains the size of the previous free block preceding it. Implemented in such a way that we // can also find the iterator for any allocation header pointer. class SortByPrevFree { public: bool operator()(const AllocationHeader* a, const AllocationHeader* b) const { if (a->GetPrevFree() < b->GetPrevFree()) return true; if (a->GetPrevFree() > b->GetPrevFree()) return false; if (a->AllocationSize() < b->AllocationSize()) return true; if (a->AllocationSize() > b->AllocationSize()) return false; return reinterpret_cast(a) < reinterpret_cast(b); } }; private: // Contains the size of the previous free block, if 0 then the memory preceding us is an // allocation. size_t prev_free_; // Allocation size of this object, 0 means that the allocation header is free memory. size_t alloc_size_; friend class FreeListSpace; }; FreeListSpace(const std::string& name, MemMap* mem_map, byte* begin, byte* end); // Removes header from the free blocks set by finding the corresponding iterator and erasing it. void RemoveFreePrev(AllocationHeader* header) EXCLUSIVE_LOCKS_REQUIRED(lock_); // Finds the allocation header corresponding to obj. AllocationHeader* GetAllocationHeader(const mirror::Object* obj); typedef std::set > FreeBlocks; // There is not footer for any allocations at the end of the space, so we keep track of how much // free space there is at the end manually. UniquePtr mem_map_; Mutex lock_ DEFAULT_MUTEX_ACQUIRED_AFTER; size_t free_end_ GUARDED_BY(lock_); FreeBlocks free_blocks_ GUARDED_BY(lock_); }; } // namespace space } // namespace gc } // namespace art #endif // ART_RUNTIME_GC_SPACE_LARGE_OBJECT_SPACE_H_