Path: blob/master/src/hotspot/share/services/mallocSiteTable.cpp
41145 views
/*1* Copyright (c) 2014, 2019, 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*/23#include "precompiled.hpp"242526#include "memory/allocation.inline.hpp"27#include "runtime/atomic.hpp"28#include "services/mallocSiteTable.hpp"2930// Malloc site hashtable buckets31MallocSiteHashtableEntry* MallocSiteTable::_table[MallocSiteTable::table_size];32const NativeCallStack* MallocSiteTable::_hash_entry_allocation_stack = NULL;33const MallocSiteHashtableEntry* MallocSiteTable::_hash_entry_allocation_site = NULL;3435// concurrent access counter36volatile int MallocSiteTable::_access_count = 0;3738// Tracking hashtable contention39NOT_PRODUCT(int MallocSiteTable::_peak_count = 0;)404142/*43* Initialize malloc site table.44* Hashtable entry is malloc'd, so it can cause infinite recursion.45* To avoid above problem, we pre-initialize a hash entry for46* this allocation site.47* The method is called during C runtime static variable initialization48* time, it is in single-threaded mode from JVM perspective.49*/50bool MallocSiteTable::initialize() {51assert((size_t)table_size <= MAX_MALLOCSITE_TABLE_SIZE, "Hashtable overflow");5253// Fake the call stack for hashtable entry allocation54assert(NMT_TrackingStackDepth > 1, "At least one tracking stack");5556// Create pseudo call stack for hashtable entry allocation57address pc[3];58if (NMT_TrackingStackDepth >= 3) {59uintx *fp = (uintx*)MallocSiteTable::allocation_at;60// On ppc64, 'fp' is a pointer to a function descriptor which is a struct of61// three native pointers where the first pointer is the real function address.62// See: http://refspecs.linuxfoundation.org/ELF/ppc64/PPC-elf64abi-1.9.html#FUNC-DES63pc[2] = (address)(fp PPC64_ONLY(BIG_ENDIAN_ONLY([0])));64}65if (NMT_TrackingStackDepth >= 2) {66uintx *fp = (uintx*)MallocSiteTable::lookup_or_add;67pc[1] = (address)(fp PPC64_ONLY(BIG_ENDIAN_ONLY([0])));68}69uintx *fp = (uintx*)MallocSiteTable::new_entry;70pc[0] = (address)(fp PPC64_ONLY(BIG_ENDIAN_ONLY([0])));7172static const NativeCallStack stack(pc, MIN2(((int)(sizeof(pc) / sizeof(address))), ((int)NMT_TrackingStackDepth)));73static const MallocSiteHashtableEntry entry(stack, mtNMT);7475assert(_hash_entry_allocation_stack == NULL &&76_hash_entry_allocation_site == NULL,77"Already initailized");7879_hash_entry_allocation_stack = &stack;80_hash_entry_allocation_site = &entry;8182// Add the allocation site to hashtable.83int index = hash_to_index(entry.hash());84_table[index] = const_cast<MallocSiteHashtableEntry*>(&entry);8586return true;87}8889// Walks entries in the hashtable.90// It stops walk if the walker returns false.91bool MallocSiteTable::walk(MallocSiteWalker* walker) {92MallocSiteHashtableEntry* head;93for (int index = 0; index < table_size; index ++) {94head = _table[index];95while (head != NULL) {96if (!walker->do_malloc_site(head->peek())) {97return false;98}99head = (MallocSiteHashtableEntry*)head->next();100}101}102return true;103}104105/*106* The hashtable does not have deletion policy on individual entry,107* and each linked list node is inserted via compare-and-swap,108* so each linked list is stable, the contention only happens109* at the end of linked list.110* This method should not return NULL under normal circumstance.111* If NULL is returned, it indicates:112* 1. Out of memory, it cannot allocate new hash entry.113* 2. Overflow hash bucket.114* Under any of above circumstances, caller should handle the situation.115*/116MallocSite* MallocSiteTable::lookup_or_add(const NativeCallStack& key, size_t* bucket_idx,117size_t* pos_idx, MEMFLAGS flags) {118assert(flags != mtNone, "Should have a real memory type");119const unsigned int hash = key.calculate_hash();120const unsigned int index = hash_to_index(hash);121*bucket_idx = (size_t)index;122*pos_idx = 0;123124// First entry for this hash bucket125if (_table[index] == NULL) {126MallocSiteHashtableEntry* entry = new_entry(key, flags);127// OOM check128if (entry == NULL) return NULL;129130// swap in the head131if (Atomic::replace_if_null(&_table[index], entry)) {132return entry->data();133}134135delete entry;136}137138MallocSiteHashtableEntry* head = _table[index];139while (head != NULL && (*pos_idx) <= MAX_BUCKET_LENGTH) {140if (head->hash() == hash) {141MallocSite* site = head->data();142if (site->flag() == flags && site->equals(key)) {143return head->data();144}145}146147if (head->next() == NULL && (*pos_idx) < MAX_BUCKET_LENGTH) {148MallocSiteHashtableEntry* entry = new_entry(key, flags);149// OOM check150if (entry == NULL) return NULL;151if (head->atomic_insert(entry)) {152(*pos_idx) ++;153return entry->data();154}155// contended, other thread won156delete entry;157}158head = (MallocSiteHashtableEntry*)head->next();159(*pos_idx) ++;160}161return NULL;162}163164// Access malloc site165MallocSite* MallocSiteTable::malloc_site(size_t bucket_idx, size_t pos_idx) {166assert(bucket_idx < table_size, "Invalid bucket index");167MallocSiteHashtableEntry* head = _table[bucket_idx];168for (size_t index = 0;169index < pos_idx && head != NULL;170index++, head = (MallocSiteHashtableEntry*)head->next()) {}171assert(head != NULL, "Invalid position index");172return head->data();173}174175// Allocates MallocSiteHashtableEntry object. Special call stack176// (pre-installed allocation site) has to be used to avoid infinite177// recursion.178MallocSiteHashtableEntry* MallocSiteTable::new_entry(const NativeCallStack& key, MEMFLAGS flags) {179void* p = AllocateHeap(sizeof(MallocSiteHashtableEntry), mtNMT,180*hash_entry_allocation_stack(), AllocFailStrategy::RETURN_NULL);181return ::new (p) MallocSiteHashtableEntry(key, flags);182}183184void MallocSiteTable::reset() {185for (int index = 0; index < table_size; index ++) {186MallocSiteHashtableEntry* head = _table[index];187_table[index] = NULL;188delete_linked_list(head);189}190191_hash_entry_allocation_stack = NULL;192_hash_entry_allocation_site = NULL;193}194195void MallocSiteTable::delete_linked_list(MallocSiteHashtableEntry* head) {196MallocSiteHashtableEntry* p;197while (head != NULL) {198p = head;199head = (MallocSiteHashtableEntry*)head->next();200if (p != hash_entry_allocation_site()) {201delete p;202}203}204}205206void MallocSiteTable::shutdown() {207AccessLock locker(&_access_count);208locker.exclusiveLock();209reset();210}211212bool MallocSiteTable::walk_malloc_site(MallocSiteWalker* walker) {213assert(walker != NULL, "NuLL walker");214AccessLock locker(&_access_count);215if (locker.sharedLock()) {216NOT_PRODUCT(_peak_count = MAX2(_peak_count, _access_count);)217return walk(walker);218}219return false;220}221222223void MallocSiteTable::AccessLock::exclusiveLock() {224int target;225int val;226227assert(_lock_state != ExclusiveLock, "Can only call once");228assert(*_lock >= 0, "Can not content exclusive lock");229230// make counter negative to block out shared locks231do {232val = *_lock;233target = _MAGIC_ + *_lock;234} while (Atomic::cmpxchg(_lock, val, target) != val);235236// wait for all readers to exit237while (*_lock != _MAGIC_) {238#ifdef _WINDOWS239os::naked_short_sleep(1);240#else241os::naked_yield();242#endif243}244_lock_state = ExclusiveLock;245}246247void MallocSiteTable::print_tuning_statistics(outputStream* st) {248249AccessLock locker(&_access_count);250if (locker.sharedLock()) {251// Total number of allocation sites, include empty sites252int total_entries = 0;253// Number of allocation sites that have all memory freed254int empty_entries = 0;255// Number of captured call stack distribution256int stack_depth_distribution[NMT_TrackingStackDepth + 1] = { 0 };257// Chain lengths258int lengths[table_size] = { 0 };259260for (int i = 0; i < table_size; i ++) {261int this_chain_length = 0;262const MallocSiteHashtableEntry* head = _table[i];263while (head != NULL) {264total_entries ++;265this_chain_length ++;266if (head->size() == 0) {267empty_entries ++;268}269const int callstack_depth = head->peek()->call_stack()->frames();270assert(callstack_depth >= 0 && callstack_depth <= NMT_TrackingStackDepth,271"Sanity (%d)", callstack_depth);272stack_depth_distribution[callstack_depth] ++;273head = head->next();274}275lengths[i] = this_chain_length;276}277278st->print_cr("Malloc allocation site table:");279st->print_cr("\tTotal entries: %d", total_entries);280st->print_cr("\tEmpty entries: %d (%2.2f%%)", empty_entries, ((float)empty_entries * 100) / total_entries);281st->cr();282283// We report the hash distribution (chain length distribution) of the n shortest chains284// - under the assumption that this usually contains all lengths. Reporting threshold285// is 20, and the expected avg chain length is 5..6 (see table size).286static const int chain_length_threshold = 20;287int chain_length_distribution[chain_length_threshold] = { 0 };288int over_threshold = 0;289int longest_chain_length = 0;290for (int i = 0; i < table_size; i ++) {291if (lengths[i] >= chain_length_threshold) {292over_threshold ++;293} else {294chain_length_distribution[lengths[i]] ++;295}296longest_chain_length = MAX2(longest_chain_length, lengths[i]);297}298299st->print_cr("Hash distribution:");300if (chain_length_distribution[0] == 0) {301st->print_cr("no empty buckets.");302} else {303st->print_cr("%d buckets are empty.", chain_length_distribution[0]);304}305for (int len = 1; len < MIN2(longest_chain_length + 1, chain_length_threshold); len ++) {306st->print_cr("%2d %s: %d.", len, (len == 1 ? " entry" : "entries"), chain_length_distribution[len]);307}308if (longest_chain_length >= chain_length_threshold) {309st->print_cr(">=%2d entries: %d.", chain_length_threshold, over_threshold);310}311st->print_cr("most entries: %d.", longest_chain_length);312st->cr();313314st->print_cr("Call stack depth distribution:");315for (int i = 0; i <= NMT_TrackingStackDepth; i ++) {316st->print_cr("\t%d: %d", i, stack_depth_distribution[i]);317}318st->cr();319} // lock320}321322323bool MallocSiteHashtableEntry::atomic_insert(MallocSiteHashtableEntry* entry) {324return Atomic::replace_if_null(&_next, entry);325}326327328