Path: blob/master/runtime/gc_trace/TgcScavenger.cpp
5985 views
/*******************************************************************************1* Copyright (c) 1991, 2019 IBM Corp. and others2*3* This program and the accompanying materials are made available under4* the terms of the Eclipse Public License 2.0 which accompanies this5* distribution and is available at https://www.eclipse.org/legal/epl-2.0/6* or the Apache License, Version 2.0 which accompanies this distribution and7* is available at https://www.apache.org/licenses/LICENSE-2.0.8*9* This Source Code may also be made available under the following10* Secondary Licenses when the conditions for such availability set11* forth in the Eclipse Public License, v. 2.0 are satisfied: GNU12* General Public License, version 2 with the GNU Classpath13* Exception [1] and GNU General Public License, version 2 with the14* OpenJDK Assembly Exception [2].15*16* [1] https://www.gnu.org/software/classpath/license.html17* [2] http://openjdk.java.net/legal/assembly-exception.html18*19* SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 OR LicenseRef-GPL-2.0 WITH Assembly-exception20*******************************************************************************/2122#include "j9.h"23#include "j9cfg.h"24#include "j9port.h"25#include "Tgc.hpp"26#include "mmhook.h"2728#if defined(J9VM_GC_MODRON_SCAVENGER)29#include "GCExtensions.hpp"30#include "MemorySubSpace.hpp"31#include "MemorySubSpaceRegionIterator.hpp"32#include "TgcExtensions.hpp"33#include "HeapRegionDescriptor.hpp"34#include "MemorySpace.hpp"35#include "ScavengerStats.hpp"36#include "ObjectHeapBufferedIterator.hpp"3738#define MAX_AGE (OBJECT_HEADER_AGE_MAX+1)3940/**41* Structure to hold details of occurrences42* of a class within the survivor space43*/44typedef struct ClassEntry {45struct ClassEntry *next;4647J9Class *clazz;48UDATA count[MAX_AGE];49} ClassEntry;5051/**52* Find the occurrence of clazz inside classList53* (if one exists)54*/55static ClassEntry *56findClassInList(ClassEntry *classList, J9Class *clazz)57{58ClassEntry *mover;5960for(mover = classList; mover; mover = mover->next) {61if(mover->clazz == clazz) {62return mover;63}64}6566/* we haven't found the class in the list */67return NULL;68}6970/**71* Add a new ClassEntry object to the head of72* classList, setting its class to clazz73*/74static ClassEntry *75addClassEntry(J9VMThread *vmThread, ClassEntry *classList, J9Class *clazz, UDATA age)76{77MM_Forge *forge = MM_GCExtensions::getExtensions(vmThread->javaVM)->getForge();7879ClassEntry *newEntry = (ClassEntry *) forge->allocate((UDATA) sizeof(ClassEntry), MM_AllocationCategory::DIAGNOSTIC, J9_GET_CALLSITE());80if (newEntry) {81memset(newEntry, 0, sizeof(ClassEntry));82} else {83/* we have failed to allocate */84return NULL;85}8687/* add to the head of the list */88newEntry->next = classList;8990/* setup the contents */91newEntry->clazz = clazz;92newEntry->count[age] = 1;9394return newEntry;95}9697/**98* Count the number of objects within a classEntry99*/100static UDATA101countObjects(ClassEntry *classEntry)102{103UDATA count = 0, i;104105for(i=0; i<MAX_AGE; i++) {106count += classEntry->count[i];107}108return count;109}110111/**112* Print a histogram of the objects using classList113*/114static void115printHistogram(J9VMThread *vmThread, ClassEntry *classList)116{117ClassEntry *mover;118UDATA objectCount = 0, i;119MM_TgcExtensions *tgcExtensions = MM_TgcExtensions::getExtensions(vmThread);120121tgcExtensions->printf("\n{SCAV: tgcScavenger OBJECT HISTOGRAM}\n");122tgcExtensions->printf("\n{SCAV: | class | instances of age 0-%zu in semi-space |\n", (UDATA)OBJECT_HEADER_AGE_MAX);123mover = classList;124125while(mover) {126/* print the results for that class */127tgcExtensions->printf("{SCAV: ");128tgcPrintClass(vmThread->javaVM, mover->clazz);129for(i=0; i<MAX_AGE; i++) {130tgcExtensions->printf(" %zu", mover->count[i]);131}132tgcExtensions->printf("\n");133/* update the total object count */134objectCount += countObjects(mover);135136/* get the next class */137mover = mover->next;138}139tgcExtensions->printf("{SCAV: Total objects in semispace = \"%zu\"\n\n", objectCount);140141}142143/**144* Free all the memory associated with classList145*/146static void147deleteClassList(J9VMThread *vmThread, ClassEntry *classList)148{149MM_Forge *forge = MM_GCExtensions::getExtensions(vmThread->javaVM)->getForge();150ClassEntry *entry, *mover;151152if(!classList) {153return;154}155156entry = classList;157158while(entry) {159mover = entry->next;160forge->free(entry);161entry = mover;162}163}164165/**166* Report object histogram following scavenger collection.167* When tgcScavenger is enabled, this function is called following each168* scavenger collection. The function walks the survivor space keeping tally169* of each class of object it encounters, and the relative age of each170* instance of that object (how many times it has been copied). The information171* is output as a simple histogram suitable for loading into a spreadsheet172* or similar for further analysis.173*/174static void175tgcHookScavengerReportObjectHistogram(J9HookInterface** hook, UDATA eventNum, void* eventData, void* userData)176{177MM_LocalGCEndEvent* event = (MM_LocalGCEndEvent*)eventData;178J9VMThread *vmThread = (J9VMThread*)event->currentThread->_language_vmthread;179J9JavaVM *javaVM = vmThread->javaVM;180MM_GCExtensions *extensions = MM_GCExtensions::getExtensions(javaVM);181MM_TgcExtensions *tgcExtensions = MM_TgcExtensions::getExtensions(extensions);182bool shouldReport = false;183184ClassEntry *classList = NULL;185MM_MemorySubSpace *subspace = ((MM_MemorySubSpace *)event->subSpace)->getDefaultMemorySubSpace();186GC_MemorySubSpaceRegionIterator regionIterator(subspace);187MM_HeapRegionDescriptor *region = NULL;188189while(NULL != (region = regionIterator.nextRegion())) {190GC_ObjectHeapBufferedIterator objectHeapIterator(extensions, region);191J9Object *object = NULL;192193shouldReport = true;194while(NULL != (object = objectHeapIterator.nextObject())) {195/* iterate over objects in the segment */196UDATA objectAge = extensions->objectModel.getObjectAge(object);197ClassEntry *entry = findClassInList(classList, J9GC_J9OBJECT_CLAZZ(object, extensions));198if(NULL != entry) {199/* increment the appropriate struct */200entry->count[objectAge] += 1;201} else {202/* add it to the list */203entry = addClassEntry(vmThread, classList, J9GC_J9OBJECT_CLAZZ(object, extensions), objectAge);204if(!entry) {205/* whoops! */206tgcExtensions->printf("Failed to allocate for histogram!\n");207deleteClassList(vmThread, classList);208return;209}210/* reset the classList to point at the new head */211classList = entry;212}213}214}215216if (shouldReport) {217/* done iterating - print out the results */218printHistogram(vmThread, classList);219}220/* free all the memory we allocated earlier */221deleteClassList(vmThread, classList);222}223224225static void226tgcHookScavengerFlipSizeHistogram(J9HookInterface** hook, UDATA eventNum, void* eventData, void* userData)227{228MM_ScavengeEndEvent* event = (MM_ScavengeEndEvent*)eventData;229MM_GCExtensions *extensions = MM_GCExtensions::getExtensions(event->currentThread);230MM_TgcExtensions *tgcExtensions = MM_TgcExtensions::getExtensions(extensions);231232tgcExtensions->printf("Scavenger Copy Bytes by Object Age:\n ");233for (IDATA i = -1; i <= OBJECT_HEADER_AGE_MAX; ++i) {234tgcExtensions->printf(" %9zi ", i);235}236tgcExtensions->printf("\n_");237for (UDATA i = 0; i <= OBJECT_HEADER_AGE_MAX+1; ++i) {238tgcExtensions->printf("___________");239}240tgcExtensions->printf("_\n");241242MM_ScavengerStats *stats = &extensions->scavengerStats;243for (UDATA j = 0; j < SCAVENGER_FLIP_HISTORY_SIZE; ++j) {244tgcExtensions->printf(" ");245246/* Flipped bytes */247for (UDATA i = 0; i <= OBJECT_HEADER_AGE_MAX+1; ++i) {248/* Blank out the top left table elements because data will not be populated for this cell until the next scavenge. */249if ((0 == j) && (0 == i)) {250tgcExtensions->printf(" ");251} else {252char tenureChar = ' ';253if (i > 0 && (0 != (((UDATA)1 << (i-1)) & stats->getFlipHistory(j)->_tenureMask))) {254tenureChar = '*';255}256tgcExtensions->printf(" %9zu%c", stats->getFlipHistory(j)->_flipBytes[i], tenureChar);257}258}259tgcExtensions->printf(" \n ");260261/* Tenured bytes */262for (UDATA i = 0; i <= OBJECT_HEADER_AGE_MAX+1; ++i) {263/* Blank out the top left table elements because data will not be populated for this cell until the next scavenge. */264if ((0 == j) && (0 == i)) {265tgcExtensions->printf(" ");266} else {267tgcExtensions->printf(" %9zu ", stats->getFlipHistory(j)->_tenureBytes[i]);268}269}270tgcExtensions->printf(" \n____________");271272/* Survival rate */273for (UDATA i = 1; i <= OBJECT_HEADER_AGE_MAX+1; ++i) {274if (j < SCAVENGER_FLIP_HISTORY_SIZE - 1) {275UDATA currentFlipBytes = stats->getFlipHistory(j)->_flipBytes[i];276UDATA currentTenureBytes = stats->getFlipHistory(j)->_tenureBytes[i];277UDATA previousFlipBytes = stats->getFlipHistory(j+1)->_flipBytes[i-1];278279double survivalPercentage = 0.0;280if (previousFlipBytes != 0) {281survivalPercentage = ((double)(currentFlipBytes + currentTenureBytes) / (double)previousFlipBytes) * 100.0;282}283284const char* underscorePadding = "";285if (99.9995 <= survivalPercentage) {286underscorePadding = "_";287} else if (9.9995 <= survivalPercentage) {288underscorePadding = "__";289} else {290underscorePadding = "___";291}292293tgcExtensions->printf("__%s%.3lf%%", underscorePadding, survivalPercentage);294} else {295tgcExtensions->printf("___________");296}297}298tgcExtensions->printf("_\n");299}300}301302static void303tgcHookScavengerDiscardedSpace(J9HookInterface** hook, UDATA eventNum, void* eventData, void* userData)304{305MM_ScavengeEndEvent* event = (MM_ScavengeEndEvent*)eventData;306MM_GCExtensions *extensions = MM_GCExtensions::getExtensions(event->currentThread);307MM_TgcExtensions *tgcExtensions = MM_TgcExtensions::getExtensions(extensions);308309MM_ScavengerStats *stats = &extensions->scavengerStats;310311tgcExtensions->printf("\n");312/* If _survivorTLHRemainderCount/_tenureTLHRemainderCount gets too high (in 1000s),313* it may indicate that discard thresholds are too low (and possibly causing contention while popping/pushing scan queue314*/315tgcExtensions->printf("Scavenger flipped=%zu discard=%zu TLHRemainderReuse=%zu\n", stats->_flipBytes, stats->_flipDiscardBytes, stats->_survivorTLHRemainderCount);316tgcExtensions->printf("Scavenger tenured=%zu discard=%zu TLHRemainderReuse=%zu\n", stats->_tenureAggregateBytes, stats->_tenureDiscardBytes, stats->_tenureTLHRemainderCount);317}318319static void320tgcHookScavengerAllocationPaths(J9HookInterface** hook, UDATA eventNum, void* eventData, void* userData)321{322MM_ScavengeEndEvent* event = (MM_ScavengeEndEvent*)eventData;323MM_GCExtensions *extensions = MM_GCExtensions::getExtensions(event->currentThread);324MM_TgcExtensions *tgcExtensions = MM_TgcExtensions::getExtensions(extensions);325326MM_ScavengerStats *stats = &extensions->scavengerStats;327tgcExtensions->printf("\n");328tgcExtensions->printf("Scavenger semi space allocation path: large=%zu, small=%zu\n", stats->_semiSpaceAllocationCountLarge, stats->_semiSpaceAllocationCountSmall);329tgcExtensions->printf("Scavenger tenure space allocation path: large=%zu, small=%zu\n", stats->_tenureSpaceAllocationCountLarge, stats->_tenureSpaceAllocationCountSmall);330tgcExtensions->printf("\n");331}332333/**334* Initialize scavenger tgc tracing.335* Initializes the TgcScavengerExtensions object associated with scavenger tgc tracing. Attaches hooks336* to the appropriate functions handling events used by scavenger tgc tracing.337*/338bool339tgcScavengerInitialize(J9JavaVM *javaVM)340{341MM_GCExtensions *extensions = MM_GCExtensions::getExtensions(javaVM);342bool result = true;343344J9HookInterface** omrHooks = J9_HOOK_INTERFACE(extensions->omrHookInterface);345/* Object histogram is expensive, but for legacy reasons we still support it. Other, cheaper, scavenger stats have been346* given special options, but also reported through this generic scavenge tgc options. */347(*omrHooks)->J9HookRegisterWithCallSite(omrHooks, J9HOOK_MM_OMR_LOCAL_GC_END, tgcHookScavengerReportObjectHistogram, OMR_GET_CALLSITE(), NULL);348349return result;350}351352/**353* Initialize scavenger copied objects tgc tracing.354* Attaches hooks to the appropriate functions handling events used by scavenger copied object tgc tracing.355*/356bool357tgcScavengerSurvivalStatsInitialize(J9JavaVM *javaVM)358{359MM_GCExtensions *extensions = MM_GCExtensions::getExtensions(javaVM);360bool result = true;361362J9HookInterface** privateHooks = J9_HOOK_INTERFACE(extensions->privateHookInterface);363(*privateHooks)->J9HookRegisterWithCallSite(privateHooks, J9HOOK_MM_PRIVATE_SCAVENGE_END, tgcHookScavengerFlipSizeHistogram, OMR_GET_CALLSITE(), NULL);364365return result;366}367368/**369* Initialize scavenger allocate and discard stats370* Attaches hooks to the appropriate functions handling events used by scavenger allocate and discard tgc tracing.371*/372bool373tgcScavengerMemoryStatsInitialize(J9JavaVM *javaVM)374{375MM_GCExtensions *extensions = MM_GCExtensions::getExtensions(javaVM);376bool result = true;377378J9HookInterface** privateHooks = J9_HOOK_INTERFACE(extensions->privateHookInterface);379(*privateHooks)->J9HookRegisterWithCallSite(privateHooks, J9HOOK_MM_PRIVATE_SCAVENGE_END, tgcHookScavengerDiscardedSpace, OMR_GET_CALLSITE(), NULL);380(*privateHooks)->J9HookRegisterWithCallSite(privateHooks, J9HOOK_MM_PRIVATE_SCAVENGE_END, tgcHookScavengerAllocationPaths, OMR_GET_CALLSITE(), NULL);381382return result;383}384385#endif /* J9VM_GC_MODRON_SCAVENGER */386387388