Path: blob/master/src/hotspot/share/services/mallocTracker.hpp
41144 views
/*1* Copyright (c) 2014, 2021, Oracle and/or its affiliates. All rights reserved.2* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.3*4* This code is free software; you can redistribute it and/or modify it5* under the terms of the GNU General Public License version 2 only, as6* published by the Free Software Foundation.7*8* This code is distributed in the hope that it will be useful, but WITHOUT9* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or10* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License11* version 2 for more details (a copy is included in the LICENSE file that12* accompanied this code).13*14* You should have received a copy of the GNU General Public License version15* 2 along with this work; if not, write to the Free Software Foundation,16* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.17*18* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA19* or visit www.oracle.com if you need additional information or have any20* questions.21*22*/2324#ifndef SHARE_SERVICES_MALLOCTRACKER_HPP25#define SHARE_SERVICES_MALLOCTRACKER_HPP2627#if INCLUDE_NMT2829#include "memory/allocation.hpp"30#include "runtime/atomic.hpp"31#include "runtime/threadCritical.hpp"32#include "services/nmtCommon.hpp"33#include "utilities/nativeCallStack.hpp"3435/*36* This counter class counts memory allocation and deallocation,37* records total memory allocation size and number of allocations.38* The counters are updated atomically.39*/40class MemoryCounter {41private:42volatile size_t _count;43volatile size_t _size;4445DEBUG_ONLY(volatile size_t _peak_count;)46DEBUG_ONLY(volatile size_t _peak_size; )4748public:49MemoryCounter() : _count(0), _size(0) {50DEBUG_ONLY(_peak_count = 0;)51DEBUG_ONLY(_peak_size = 0;)52}5354inline void allocate(size_t sz) {55size_t cnt = Atomic::add(&_count, size_t(1), memory_order_relaxed);56if (sz > 0) {57size_t sum = Atomic::add(&_size, sz, memory_order_relaxed);58DEBUG_ONLY(update_peak_size(sum);)59}60DEBUG_ONLY(update_peak_count(cnt);)61}6263inline void deallocate(size_t sz) {64assert(count() > 0, "Nothing allocated yet");65assert(size() >= sz, "deallocation > allocated");66Atomic::dec(&_count, memory_order_relaxed);67if (sz > 0) {68Atomic::sub(&_size, sz, memory_order_relaxed);69}70}7172inline void resize(ssize_t sz) {73if (sz != 0) {74assert(sz >= 0 || size() >= size_t(-sz), "Must be");75size_t sum = Atomic::add(&_size, size_t(sz), memory_order_relaxed);76DEBUG_ONLY(update_peak_size(sum);)77}78}7980inline size_t count() const { return Atomic::load(&_count); }81inline size_t size() const { return Atomic::load(&_size); }8283#ifdef ASSERT84void update_peak_count(size_t cnt);85void update_peak_size(size_t sz);86size_t peak_count() const;87size_t peak_size() const;88#endif // ASSERT89};9091/*92* Malloc memory used by a particular subsystem.93* It includes the memory acquired through os::malloc()94* call and arena's backing memory.95*/96class MallocMemory {97private:98MemoryCounter _malloc;99MemoryCounter _arena;100101public:102MallocMemory() { }103104inline void record_malloc(size_t sz) {105_malloc.allocate(sz);106}107108inline void record_free(size_t sz) {109_malloc.deallocate(sz);110}111112inline void record_new_arena() {113_arena.allocate(0);114}115116inline void record_arena_free() {117_arena.deallocate(0);118}119120inline void record_arena_size_change(ssize_t sz) {121_arena.resize(sz);122}123124inline size_t malloc_size() const { return _malloc.size(); }125inline size_t malloc_count() const { return _malloc.count();}126inline size_t arena_size() const { return _arena.size(); }127inline size_t arena_count() const { return _arena.count(); }128129DEBUG_ONLY(inline const MemoryCounter& malloc_counter() const { return _malloc; })130DEBUG_ONLY(inline const MemoryCounter& arena_counter() const { return _arena; })131};132133class MallocMemorySummary;134135// A snapshot of malloc'd memory, includes malloc memory136// usage by types and memory used by tracking itself.137class MallocMemorySnapshot : public ResourceObj {138friend class MallocMemorySummary;139140private:141MallocMemory _malloc[mt_number_of_types];142MemoryCounter _tracking_header;143144145public:146inline MallocMemory* by_type(MEMFLAGS flags) {147int index = NMTUtil::flag_to_index(flags);148return &_malloc[index];149}150151inline MemoryCounter* malloc_overhead() {152return &_tracking_header;153}154155// Total malloc'd memory amount156size_t total() const;157// Total malloc'd memory used by arenas158size_t total_arena() const;159160inline size_t thread_count() const {161MallocMemorySnapshot* s = const_cast<MallocMemorySnapshot*>(this);162return s->by_type(mtThreadStack)->malloc_count();163}164165void copy_to(MallocMemorySnapshot* s) {166// Need to make sure that mtChunks don't get deallocated while the167// copy is going on, because their size is adjusted using this168// buffer in make_adjustment().169ThreadCritical tc;170s->_tracking_header = _tracking_header;171for (int index = 0; index < mt_number_of_types; index ++) {172s->_malloc[index] = _malloc[index];173}174}175176// Make adjustment by subtracting chunks used by arenas177// from total chunks to get total free chunk size178void make_adjustment();179};180181/*182* This class is for collecting malloc statistics at summary level183*/184class MallocMemorySummary : AllStatic {185private:186// Reserve memory for placement of MallocMemorySnapshot object187static size_t _snapshot[CALC_OBJ_SIZE_IN_TYPE(MallocMemorySnapshot, size_t)];188189public:190static void initialize();191192static inline void record_malloc(size_t size, MEMFLAGS flag) {193as_snapshot()->by_type(flag)->record_malloc(size);194}195196static inline void record_free(size_t size, MEMFLAGS flag) {197as_snapshot()->by_type(flag)->record_free(size);198}199200static inline void record_new_arena(MEMFLAGS flag) {201as_snapshot()->by_type(flag)->record_new_arena();202}203204static inline void record_arena_free(MEMFLAGS flag) {205as_snapshot()->by_type(flag)->record_arena_free();206}207208static inline void record_arena_size_change(ssize_t size, MEMFLAGS flag) {209as_snapshot()->by_type(flag)->record_arena_size_change(size);210}211212static void snapshot(MallocMemorySnapshot* s) {213as_snapshot()->copy_to(s);214s->make_adjustment();215}216217// Record memory used by malloc tracking header218static inline void record_new_malloc_header(size_t sz) {219as_snapshot()->malloc_overhead()->allocate(sz);220}221222static inline void record_free_malloc_header(size_t sz) {223as_snapshot()->malloc_overhead()->deallocate(sz);224}225226// The memory used by malloc tracking headers227static inline size_t tracking_overhead() {228return as_snapshot()->malloc_overhead()->size();229}230231static MallocMemorySnapshot* as_snapshot() {232return (MallocMemorySnapshot*)_snapshot;233}234};235236237/*238* Malloc tracking header.239* To satisfy malloc alignment requirement, NMT uses 2 machine words for tracking purpose,240* which ensures 8-bytes alignment on 32-bit systems and 16-bytes on 64-bit systems (Product build).241*/242243class MallocHeader {244#ifdef _LP64245size_t _size : 64;246size_t _flags : 8;247size_t _pos_idx : 16;248size_t _bucket_idx: 40;249#define MAX_MALLOCSITE_TABLE_SIZE right_n_bits(40)250#define MAX_BUCKET_LENGTH right_n_bits(16)251#else252size_t _size : 32;253size_t _flags : 8;254size_t _pos_idx : 8;255size_t _bucket_idx: 16;256#define MAX_MALLOCSITE_TABLE_SIZE right_n_bits(16)257#define MAX_BUCKET_LENGTH right_n_bits(8)258#endif // _LP64259260public:261MallocHeader(size_t size, MEMFLAGS flags, const NativeCallStack& stack, NMT_TrackingLevel level) {262assert(sizeof(MallocHeader) == sizeof(void*) * 2,263"Wrong header size");264265if (level == NMT_minimal) {266return;267}268269_flags = NMTUtil::flag_to_index(flags);270set_size(size);271if (level == NMT_detail) {272size_t bucket_idx;273size_t pos_idx;274if (record_malloc_site(stack, size, &bucket_idx, &pos_idx, flags)) {275assert(bucket_idx <= MAX_MALLOCSITE_TABLE_SIZE, "Overflow bucket index");276assert(pos_idx <= MAX_BUCKET_LENGTH, "Overflow bucket position index");277_bucket_idx = bucket_idx;278_pos_idx = pos_idx;279}280}281282MallocMemorySummary::record_malloc(size, flags);283MallocMemorySummary::record_new_malloc_header(sizeof(MallocHeader));284}285286inline size_t size() const { return _size; }287inline MEMFLAGS flags() const { return (MEMFLAGS)_flags; }288bool get_stack(NativeCallStack& stack) const;289290// Cleanup tracking information before the memory is released.291void release() const;292293private:294inline void set_size(size_t size) {295_size = size;296}297bool record_malloc_site(const NativeCallStack& stack, size_t size,298size_t* bucket_idx, size_t* pos_idx, MEMFLAGS flags) const;299};300301302// Main class called from MemTracker to track malloc activities303class MallocTracker : AllStatic {304public:305// Initialize malloc tracker for specific tracking level306static bool initialize(NMT_TrackingLevel level);307308static bool transition(NMT_TrackingLevel from, NMT_TrackingLevel to);309310// malloc tracking header size for specific tracking level311static inline size_t malloc_header_size(NMT_TrackingLevel level) {312return (level == NMT_off) ? 0 : sizeof(MallocHeader);313}314315// Parameter name convention:316// memblock : the beginning address for user data317// malloc_base: the beginning address that includes malloc tracking header318//319// The relationship:320// memblock = (char*)malloc_base + sizeof(nmt header)321//322323// Record malloc on specified memory block324static void* record_malloc(void* malloc_base, size_t size, MEMFLAGS flags,325const NativeCallStack& stack, NMT_TrackingLevel level);326327// Record free on specified memory block328static void* record_free(void* memblock);329330// Offset memory address to header address331static inline void* get_base(void* memblock);332static inline void* get_base(void* memblock, NMT_TrackingLevel level) {333if (memblock == NULL || level == NMT_off) return memblock;334return (char*)memblock - malloc_header_size(level);335}336337// Get memory size338static inline size_t get_size(void* memblock) {339MallocHeader* header = malloc_header(memblock);340return header->size();341}342343// Get memory type344static inline MEMFLAGS get_flags(void* memblock) {345MallocHeader* header = malloc_header(memblock);346return header->flags();347}348349// Get header size350static inline size_t get_header_size(void* memblock) {351return (memblock == NULL) ? 0 : sizeof(MallocHeader);352}353354static inline void record_new_arena(MEMFLAGS flags) {355MallocMemorySummary::record_new_arena(flags);356}357358static inline void record_arena_free(MEMFLAGS flags) {359MallocMemorySummary::record_arena_free(flags);360}361362static inline void record_arena_size_change(ssize_t size, MEMFLAGS flags) {363MallocMemorySummary::record_arena_size_change(size, flags);364}365private:366static inline MallocHeader* malloc_header(void *memblock) {367assert(memblock != NULL, "NULL pointer");368MallocHeader* header = (MallocHeader*)((char*)memblock - sizeof(MallocHeader));369return header;370}371};372373#endif // INCLUDE_NMT374375376#endif // SHARE_SERVICES_MALLOCTRACKER_HPP377378379