zgu@7074: /* zgu@7074: * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved. zgu@7074: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. zgu@7074: * zgu@7074: * This code is free software; you can redistribute it and/or modify it zgu@7074: * under the terms of the GNU General Public License version 2 only, as zgu@7074: * published by the Free Software Foundation. zgu@7074: * zgu@7074: * This code is distributed in the hope that it will be useful, but WITHOUT zgu@7074: * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or zgu@7074: * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License zgu@7074: * version 2 for more details (a copy is included in the LICENSE file that zgu@7074: * accompanied this code). zgu@7074: * zgu@7074: * You should have received a copy of the GNU General Public License version zgu@7074: * 2 along with this work; if not, write to the Free Software Foundation, zgu@7074: * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. zgu@7074: * zgu@7074: * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA zgu@7074: * or visit www.oracle.com if you need additional information or have any zgu@7074: * questions. zgu@7074: * zgu@7074: */ zgu@7074: zgu@7074: #ifndef SHARE_VM_SERVICES_MALLOC_TRACKER_HPP zgu@7074: #define SHARE_VM_SERVICES_MALLOC_TRACKER_HPP zgu@7074: zgu@7074: #if INCLUDE_NMT zgu@7074: zgu@7074: #include "memory/allocation.hpp" zgu@7074: #include "runtime/atomic.hpp" zgu@7074: #include "services/nmtCommon.hpp" zgu@7074: #include "utilities/nativeCallStack.hpp" zgu@7074: zgu@7074: /* zgu@7074: * This counter class counts memory allocation and deallocation, zgu@7074: * records total memory allocation size and number of allocations. zgu@7074: * The counters are updated atomically. zgu@7074: */ zgu@7074: class MemoryCounter VALUE_OBJ_CLASS_SPEC { zgu@7074: private: zgu@7074: size_t _count; zgu@7074: size_t _size; zgu@7074: zgu@7074: DEBUG_ONLY(size_t _peak_count;) zgu@7074: DEBUG_ONLY(size_t _peak_size; ) zgu@7074: zgu@7074: public: zgu@7074: MemoryCounter() : _count(0), _size(0) { zgu@7074: DEBUG_ONLY(_peak_count = 0;) zgu@7074: DEBUG_ONLY(_peak_size = 0;) zgu@7074: } zgu@7074: zgu@7074: inline void allocate(size_t sz) { zgu@7074: Atomic::add(1, (volatile MemoryCounterType*)&_count); zgu@7074: if (sz > 0) { zgu@7074: Atomic::add((MemoryCounterType)sz, (volatile MemoryCounterType*)&_size); zgu@7074: DEBUG_ONLY(_peak_size = MAX2(_peak_size, _size)); zgu@7074: } zgu@7074: DEBUG_ONLY(_peak_count = MAX2(_peak_count, _count);) zgu@7074: } zgu@7074: zgu@7074: inline void deallocate(size_t sz) { zgu@7074: assert(_count > 0, "Negative counter"); zgu@7074: assert(_size >= sz, "Negative size"); zgu@7074: Atomic::add(-1, (volatile MemoryCounterType*)&_count); zgu@7074: if (sz > 0) { zgu@7074: Atomic::add(-(MemoryCounterType)sz, (volatile MemoryCounterType*)&_size); zgu@7074: } zgu@7074: } zgu@7074: zgu@7074: inline void resize(long sz) { zgu@7074: if (sz != 0) { zgu@7074: Atomic::add((MemoryCounterType)sz, (volatile MemoryCounterType*)&_size); zgu@7074: DEBUG_ONLY(_peak_size = MAX2(_size, _peak_size);) zgu@7074: } zgu@7074: } zgu@7074: zgu@7074: inline size_t count() const { return _count; } zgu@7074: inline size_t size() const { return _size; } zgu@7074: DEBUG_ONLY(inline size_t peak_count() const { return _peak_count; }) zgu@7074: DEBUG_ONLY(inline size_t peak_size() const { return _peak_size; }) zgu@7074: zgu@7074: }; zgu@7074: zgu@7074: /* zgu@7074: * Malloc memory used by a particular subsystem. zgu@7074: * It includes the memory acquired through os::malloc() zgu@7074: * call and arena's backing memory. zgu@7074: */ zgu@7074: class MallocMemory VALUE_OBJ_CLASS_SPEC { zgu@7074: private: zgu@7074: MemoryCounter _malloc; zgu@7074: MemoryCounter _arena; zgu@7074: zgu@7074: public: zgu@7074: MallocMemory() { } zgu@7074: zgu@7074: inline void record_malloc(size_t sz) { zgu@7074: _malloc.allocate(sz); zgu@7074: } zgu@7074: zgu@7074: inline void record_free(size_t sz) { zgu@7074: _malloc.deallocate(sz); zgu@7074: } zgu@7074: zgu@7074: inline void record_new_arena() { zgu@7074: _arena.allocate(0); zgu@7074: } zgu@7074: zgu@7074: inline void record_arena_free() { zgu@7074: _arena.deallocate(0); zgu@7074: } zgu@7074: zgu@7074: inline void record_arena_size_change(long sz) { zgu@7074: _arena.resize(sz); zgu@7074: } zgu@7074: zgu@7074: inline size_t malloc_size() const { return _malloc.size(); } zgu@7074: inline size_t malloc_count() const { return _malloc.count();} zgu@7074: inline size_t arena_size() const { return _arena.size(); } zgu@7074: inline size_t arena_count() const { return _arena.count(); } zgu@7074: zgu@7074: DEBUG_ONLY(inline const MemoryCounter& malloc_counter() const { return _malloc; }) zgu@7074: DEBUG_ONLY(inline const MemoryCounter& arena_counter() const { return _arena; }) zgu@7074: }; zgu@7074: zgu@7074: class MallocMemorySummary; zgu@7074: zgu@7074: // A snapshot of malloc'd memory, includes malloc memory zgu@7074: // usage by types and memory used by tracking itself. zgu@7074: class MallocMemorySnapshot : public ResourceObj { zgu@7074: friend class MallocMemorySummary; zgu@7074: zgu@7074: private: zgu@7074: MallocMemory _malloc[mt_number_of_types]; zgu@7074: MemoryCounter _tracking_header; zgu@7074: zgu@7074: zgu@7074: public: zgu@7074: inline MallocMemory* by_type(MEMFLAGS flags) { zgu@7074: int index = NMTUtil::flag_to_index(flags); zgu@7074: return &_malloc[index]; zgu@7074: } zgu@7074: zgu@7074: inline MallocMemory* by_index(int index) { zgu@7074: assert(index >= 0, "Index out of bound"); zgu@7074: assert(index < mt_number_of_types, "Index out of bound"); zgu@7074: return &_malloc[index]; zgu@7074: } zgu@7074: zgu@7074: inline MemoryCounter* malloc_overhead() { zgu@7074: return &_tracking_header; zgu@7074: } zgu@7074: zgu@7074: // Total malloc'd memory amount zgu@7074: size_t total() const; zgu@7074: // Total malloc'd memory used by arenas zgu@7074: size_t total_arena() const; zgu@7074: zgu@7080: inline size_t thread_count() const { zgu@7080: MallocMemorySnapshot* s = const_cast(this); zgu@7080: return s->by_type(mtThreadStack)->malloc_count(); zgu@7074: } zgu@7074: zgu@7074: void copy_to(MallocMemorySnapshot* s) { zgu@7074: s->_tracking_header = _tracking_header; zgu@7074: for (int index = 0; index < mt_number_of_types; index ++) { zgu@7074: s->_malloc[index] = _malloc[index]; zgu@7074: } zgu@7074: } zgu@7074: zgu@7074: // Make adjustment by subtracting chunks used by arenas zgu@7074: // from total chunks to get total free chunk size zgu@7074: void make_adjustment(); zgu@7074: }; zgu@7074: zgu@7074: /* zgu@7074: * This class is for collecting malloc statistics at summary level zgu@7074: */ zgu@7074: class MallocMemorySummary : AllStatic { zgu@7074: private: zgu@7074: // Reserve memory for placement of MallocMemorySnapshot object zgu@7074: static size_t _snapshot[CALC_OBJ_SIZE_IN_TYPE(MallocMemorySnapshot, size_t)]; zgu@7074: zgu@7074: public: zgu@7074: static void initialize(); zgu@7074: zgu@7074: static inline void record_malloc(size_t size, MEMFLAGS flag) { zgu@7074: as_snapshot()->by_type(flag)->record_malloc(size); zgu@7074: } zgu@7074: zgu@7074: static inline void record_free(size_t size, MEMFLAGS flag) { zgu@7074: as_snapshot()->by_type(flag)->record_free(size); zgu@7074: } zgu@7074: zgu@7074: static inline void record_new_arena(MEMFLAGS flag) { zgu@7074: as_snapshot()->by_type(flag)->record_new_arena(); zgu@7074: } zgu@7074: zgu@7074: static inline void record_arena_free(MEMFLAGS flag) { zgu@7074: as_snapshot()->by_type(flag)->record_arena_free(); zgu@7074: } zgu@7074: zgu@7074: static inline void record_arena_size_change(long size, MEMFLAGS flag) { zgu@7074: as_snapshot()->by_type(flag)->record_arena_size_change(size); zgu@7074: } zgu@7074: zgu@7074: static void snapshot(MallocMemorySnapshot* s) { zgu@7074: as_snapshot()->copy_to(s); zgu@7074: s->make_adjustment(); zgu@7074: } zgu@7074: zgu@7074: // Record memory used by malloc tracking header zgu@7074: static inline void record_new_malloc_header(size_t sz) { zgu@7074: as_snapshot()->malloc_overhead()->allocate(sz); zgu@7074: } zgu@7074: zgu@7074: static inline void record_free_malloc_header(size_t sz) { zgu@7074: as_snapshot()->malloc_overhead()->deallocate(sz); zgu@7074: } zgu@7074: zgu@7074: // The memory used by malloc tracking headers zgu@7074: static inline size_t tracking_overhead() { zgu@7074: return as_snapshot()->malloc_overhead()->size(); zgu@7074: } zgu@7074: zgu@7074: static MallocMemorySnapshot* as_snapshot() { zgu@7074: return (MallocMemorySnapshot*)_snapshot; zgu@7074: } zgu@7074: }; zgu@7074: zgu@7074: zgu@7074: /* zgu@7074: * Malloc tracking header. zgu@7074: * To satisfy malloc alignment requirement, NMT uses 2 machine words for tracking purpose, zgu@7074: * which ensures 8-bytes alignment on 32-bit systems and 16-bytes on 64-bit systems (Product build). zgu@7074: */ zgu@7074: zgu@7074: class MallocHeader VALUE_OBJ_CLASS_SPEC { zgu@7074: #ifdef _LP64 ctornqvi@7344: size_t _size : 64; zgu@7074: size_t _flags : 8; zgu@7074: size_t _pos_idx : 16; zgu@7074: size_t _bucket_idx: 40; zgu@7074: #define MAX_MALLOCSITE_TABLE_SIZE ((size_t)1 << 40) zgu@7074: #define MAX_BUCKET_LENGTH ((size_t)(1 << 16)) zgu@7074: #else ctornqvi@7344: size_t _size : 32; zgu@7074: size_t _flags : 8; zgu@7074: size_t _pos_idx : 8; zgu@7074: size_t _bucket_idx: 16; zgu@7074: #define MAX_MALLOCSITE_TABLE_SIZE ((size_t)(1 << 16)) zgu@7074: #define MAX_BUCKET_LENGTH ((size_t)(1 << 8)) zgu@7074: #endif // _LP64 zgu@7074: zgu@7074: public: ctornqvi@7344: MallocHeader(size_t size, MEMFLAGS flags, const NativeCallStack& stack, NMT_TrackingLevel level) { zgu@7074: assert(sizeof(MallocHeader) == sizeof(void*) * 2, zgu@7074: "Wrong header size"); zgu@7074: ctornqvi@7344: if (level == NMT_minimal) { ctornqvi@7344: return; ctornqvi@7344: } ctornqvi@7344: zgu@7074: _flags = flags; zgu@7074: set_size(size); ctornqvi@7344: if (level == NMT_detail) { ctornqvi@7344: size_t bucket_idx; ctornqvi@7344: size_t pos_idx; ctornqvi@7344: if (record_malloc_site(stack, size, &bucket_idx, &pos_idx)) { ctornqvi@7344: assert(bucket_idx <= MAX_MALLOCSITE_TABLE_SIZE, "Overflow bucket index"); ctornqvi@7344: assert(pos_idx <= MAX_BUCKET_LENGTH, "Overflow bucket position index"); ctornqvi@7344: _bucket_idx = bucket_idx; ctornqvi@7344: _pos_idx = pos_idx; ctornqvi@7344: } ctornqvi@7344: } ctornqvi@7344: zgu@7074: MallocMemorySummary::record_malloc(size, flags); zgu@7074: MallocMemorySummary::record_new_malloc_header(sizeof(MallocHeader)); zgu@7074: } zgu@7074: zgu@7074: inline size_t size() const { return _size; } zgu@7074: inline MEMFLAGS flags() const { return (MEMFLAGS)_flags; } zgu@7074: bool get_stack(NativeCallStack& stack) const; zgu@7074: zgu@7074: // Cleanup tracking information before the memory is released. zgu@7074: void release() const; zgu@7074: zgu@7074: private: zgu@7074: inline void set_size(size_t size) { zgu@7074: _size = size; zgu@7074: } zgu@7074: bool record_malloc_site(const NativeCallStack& stack, size_t size, zgu@7074: size_t* bucket_idx, size_t* pos_idx) const; zgu@7074: }; zgu@7074: zgu@7074: zgu@7074: // Main class called from MemTracker to track malloc activities zgu@7074: class MallocTracker : AllStatic { zgu@7074: public: zgu@7074: // Initialize malloc tracker for specific tracking level zgu@7074: static bool initialize(NMT_TrackingLevel level); zgu@7074: zgu@7074: static bool transition(NMT_TrackingLevel from, NMT_TrackingLevel to); zgu@7074: zgu@7074: // malloc tracking header size for specific tracking level zgu@7074: static inline size_t malloc_header_size(NMT_TrackingLevel level) { zgu@7074: return (level == NMT_off) ? 0 : sizeof(MallocHeader); zgu@7074: } zgu@7074: zgu@7074: // Parameter name convention: zgu@7074: // memblock : the beginning address for user data zgu@7074: // malloc_base: the beginning address that includes malloc tracking header zgu@7074: // zgu@7074: // The relationship: zgu@7074: // memblock = (char*)malloc_base + sizeof(nmt header) zgu@7074: // zgu@7074: zgu@7074: // Record malloc on specified memory block zgu@7074: static void* record_malloc(void* malloc_base, size_t size, MEMFLAGS flags, zgu@7074: const NativeCallStack& stack, NMT_TrackingLevel level); zgu@7074: zgu@7074: // Record free on specified memory block zgu@7074: static void* record_free(void* memblock); zgu@7074: zgu@7074: // Offset memory address to header address zgu@7074: static inline void* get_base(void* memblock); zgu@7074: static inline void* get_base(void* memblock, NMT_TrackingLevel level) { zgu@7074: if (memblock == NULL || level == NMT_off) return memblock; zgu@7074: return (char*)memblock - malloc_header_size(level); zgu@7074: } zgu@7074: zgu@7074: // Get memory size zgu@7074: static inline size_t get_size(void* memblock) { zgu@7074: MallocHeader* header = malloc_header(memblock); zgu@7074: return header->size(); zgu@7074: } zgu@7074: zgu@7074: // Get memory type zgu@7074: static inline MEMFLAGS get_flags(void* memblock) { zgu@7074: MallocHeader* header = malloc_header(memblock); zgu@7074: return header->flags(); zgu@7074: } zgu@7074: zgu@7074: // Get header size zgu@7074: static inline size_t get_header_size(void* memblock) { zgu@7074: return (memblock == NULL) ? 0 : sizeof(MallocHeader); zgu@7074: } zgu@7074: zgu@7074: static inline void record_new_arena(MEMFLAGS flags) { zgu@7074: MallocMemorySummary::record_new_arena(flags); zgu@7074: } zgu@7074: zgu@7074: static inline void record_arena_free(MEMFLAGS flags) { zgu@7074: MallocMemorySummary::record_arena_free(flags); zgu@7074: } zgu@7074: zgu@7074: static inline void record_arena_size_change(int size, MEMFLAGS flags) { zgu@7074: MallocMemorySummary::record_arena_size_change(size, flags); zgu@7074: } zgu@7074: private: zgu@7074: static inline MallocHeader* malloc_header(void *memblock) { zgu@7074: assert(memblock != NULL, "NULL pointer"); zgu@7074: MallocHeader* header = (MallocHeader*)((char*)memblock - sizeof(MallocHeader)); zgu@7074: return header; zgu@7074: } zgu@7074: }; zgu@7074: zgu@7074: #endif // INCLUDE_NMT zgu@7074: zgu@7074: zgu@7074: #endif //SHARE_VM_SERVICES_MALLOC_TRACKER_HPP