Mercurial > hg > heapstats
changeset 221:aba6d9899517
Bug 3331: Refactoring for memory management in HeapStats Agent
Reviewed-by: ykubota
https://github.com/HeapStats/heapstats/pull/86
author | Yasumasa Suenaga <yasuenag@gmail.com> |
---|---|
date | Mon, 27 Feb 2017 19:22:39 +0900 |
parents | b2dad9ff2e1b |
children | f3d7307b0ee3 |
files | ChangeLog agent/src/heapstats-engines/arch/arm/util.inline.hpp agent/src/heapstats-engines/arch/x86/util.inline.hpp agent/src/heapstats-engines/classContainer.cpp agent/src/heapstats-engines/classContainer.hpp agent/src/heapstats-engines/libmain.cpp agent/src/heapstats-engines/snapShotContainer.cpp agent/src/heapstats-engines/snapShotContainer.hpp agent/src/heapstats-engines/snapShotMain.cpp agent/src/heapstats-engines/snapShotMain.hpp agent/src/heapstats-engines/snapShotProcessor.cpp agent/src/heapstats-engines/util.hpp |
diffstat | 12 files changed, 267 insertions(+), 444 deletions(-) [+] |
line wrap: on
line diff
--- a/ChangeLog Thu Feb 09 22:04:01 2017 +0900 +++ b/ChangeLog Mon Feb 27 19:22:39 2017 +0900 @@ -1,3 +1,7 @@ +2017-02-27 Yasumasa Suenaga <yasuenag@gmail.com> + + * Bug 3331: Refactoring for memory management in HeapStats Agent + 2017-02-09 Yasumasa Suenaga <yasuenag@gmail.com> * Bug 3322: TClassContainer instance might be broken in multithreaded access
--- a/agent/src/heapstats-engines/arch/arm/util.inline.hpp Thu Feb 09 22:04:01 2017 +0900 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,42 +0,0 @@ -/*! - * \file util.inline.hpp - * \brief Optimized utility functions for ARM processor. - * Copyright (C) 2017 Yasumasa Suenaga - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - * - */ - -#ifndef ARM_UTIL_H -#define ARM_UTIL_H - -inline void atomic_inc(int *target, int value) { - asm volatile("1:" - " ldrex %%r0, [%0];" - " add %%r0, %%r0, %1;" - " strex %%r1, %%r0, [%0];" - " tst %%r1, %%r1;" - " bne 1b;" - : : "r"(target), "r"(value) : "memory", "cc", "%r0", "%r1"); -} - -inline int atomic_get(int *target) { - register int ret; - asm volatile("ldrex %0, [%1]" : "=r"(ret) : "r"(target) : ); - - return ret; -} - -#endif // ARM_UTIL_H
--- a/agent/src/heapstats-engines/arch/x86/util.inline.hpp Thu Feb 09 22:04:01 2017 +0900 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,46 +0,0 @@ -/*! - * \file util.inline.hpp - * \brief Optimized utility functions for x86 processor. - * Copyright (C) 2017 Yasumasa Suenaga - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - * - */ - -#ifndef X86_UTIL_H -#define X86_UTIL_H - -inline void atomic_inc(int *target, int value) { - asm volatile("lock addl %1, (%0)" : - : "r"(target), "r"(value) - : "memory", "cc"); -} - -/* - * We can use MOV operation at this point. - * LOCK'ed store operation will be occurred before load (MOV) operation. - * - * Intel (R) 64 and IA-32 Architectures Software Developer’s Manual - * Volume 3A: System Programming Guide, Part 1 - * 8.2.3.8 Locked Instructions Have a Total Order - */ -inline int atomic_get(int *target) { - register int ret; - asm volatile("movl (%1), %0" : "=r"(ret) : "r"(target) : ); - - return ret; -} - -#endif // X86_UTIL_H
--- a/agent/src/heapstats-engines/classContainer.cpp Thu Feb 09 22:04:01 2017 +0900 +++ b/agent/src/heapstats-engines/classContainer.cpp Mon Feb 27 19:22:39 2017 +0900 @@ -22,6 +22,7 @@ #include <fcntl.h> #include "globals.hpp" +#include "vmFunctions.hpp" #include "classContainer.hpp" /*! @@ -76,6 +77,9 @@ */ static char ORDER_USAGE[6] = "USAGE"; +static TClassInfoSet unloadedList; +volatile int unloadedList_lock = 0; + /*! * \briefString of DELTA order. */ @@ -94,7 +98,6 @@ needToClear = needToClr; classMap = NULL; pSender = NULL; - unloadedList = NULL; if (likely(base != NULL)) { /* Get parent container's spin lock. */ @@ -127,9 +130,6 @@ /* Create trap sender. */ pSender = conf->SnmpSend()->get() ? new TTrapSender() : NULL; - /* Create unloaded class information queue. */ - unloadedList = new TClassInfoQueue(); - /* Create thread storage key. */ if (unlikely(pthread_key_create(&clsContainerKey, NULL) != 0)) { throw 1; @@ -137,7 +137,6 @@ } catch (...) { delete classMap; delete pSender; - delete unloadedList; throw "TClassContainer initialize failed!"; } } @@ -160,7 +159,6 @@ /* Cleanup instances. */ delete classMap; delete pSender; - delete unloadedList; /* Cleanup thread storage key. */ pthread_key_delete(clsContainerKey); @@ -220,8 +218,6 @@ free(cur->className); free(cur); } - - atomic_inc(&result->numRefs, 1); return result; } @@ -256,22 +252,14 @@ strcmp(objData->className, expectData->className) == 0 && objData->clsLoaderId == expectData->clsLoaderId)) { /* Return existing data on map. */ - /* - * We should not increment reference counter because we do not add - * reference. - */ existData = expectData; } else { /* klass oop is doubling for another class. */ - removeClass(expectData); - try { - unloadedList->push(expectData); - } catch (...) { - /* - * We try to continue running without crash - * even if failed to allocate memory. - */ + spinLockWait(&unloadedList_lock); + { + unloadedList.insert(expectData); } + spinLockRelease(&unloadedList_lock); } } } @@ -301,10 +289,7 @@ /* Broadcast to each local container. */ for (TLocalClassContainer::iterator it = localContainers.begin(); it != localContainers.end(); it++) { - // We should skip myself if "this" ptr is in local container. - if (*it != this) { - (*it)->pushNewClass(klassOop, objData); - } + (*it)->pushNewClass(klassOop, objData); } } /* Release spin lock of containers queue. */ @@ -313,25 +298,12 @@ } /*! - * \brief Mark class in container to remove class. - * \param target [in] Remove class data. - */ -void TClassContainer::popClass(TObjectData *target) { - /* - * This function isn't remove item from map. - * Remove item and deallocate memory at "commitClassChange". - */ - target->isRemoved = true; -} - -/*! * \brief Remove class from container. * \param target [in] Remove class data. */ void TClassContainer::removeClass(TObjectData *target) { /* Remove item from map. Please callee has container's lock. */ classMap->erase(target->klassOop); - atomic_inc(&target->numRefs, -1); /* Get spin lock of containers queue. */ spinLockWait(&queueLock); @@ -339,76 +311,35 @@ /* Broadcast to each local container. */ for (TLocalClassContainer::iterator it = localContainers.begin(); it != localContainers.end(); it++) { - // We should skip myself if "this" ptr is in local container. - if (*it != this) { - /* Get local container's spin lock. */ - spinLockWait(&(*it)->lockval); - { - (*it)->classMap->erase(target->klassOop); - atomic_inc(&target->numRefs, -1); - } - /* Release local container's spin lock. */ - spinLockRelease(&(*it)->lockval); - } - } - } - /* Release spin lock of containers queue. */ - spinLockRelease(&queueLock); -} - -/*! - * \brief Remove all-class from container. - */ -void TClassContainer::allClear(void) { - /* Get spin lock of containers queue. */ - spinLockWait(&queueLock); - { - /* Broadcast to each local container. */ - for (TLocalClassContainer::iterator it = localContainers.begin(); - it != localContainers.end(); it++) { /* Get local container's spin lock. */ spinLockWait(&(*it)->lockval); - { (*it)->classMap->clear(); } + { (*it)->classMap->erase(target->klassOop); } /* Release local container's spin lock. */ spinLockRelease(&(*it)->lockval); } } /* Release spin lock of containers queue. */ spinLockRelease(&queueLock); - - /* Get class container's spin lock. */ - spinLockWait(&lockval); - { - /* Free allocated memory at class map. */ - for (TClassMap::iterator cur = classMap->begin(); cur != classMap->end(); - ++cur) { - TObjectData *pos = (*cur).second; - - if (likely(pos != NULL)) { - /* Decrement reference from this class map. */ - atomic_inc(&pos->numRefs, -1); +} - if (atomic_get(&pos->numRefs) == 0) { - free(pos->className); - free(pos); - } - } - } +/*! + * \brief Remove all-class from container. + * This function will be called from d'tor of TClassContainer. + * So we do not get any lock because d'tor calls at Agent_OnUnload. + */ +void TClassContainer::allClear(void) { + /* Add all TObjectData pointers in parent container map to unloadedList */ + for (TClassMap::iterator cur = classMap->begin(); cur != classMap->end(); + ++cur) { + unloadedList.insert(cur->second); + } - /* Free allocated memory at unloaded list. */ - while (!unloadedList->empty()) { - TObjectData *pos = unloadedList->front(); - unloadedList->pop(); - - free(pos->className); - free(pos); - } - - /* Clear all class. */ - classMap->clear(); + /* Release all memory for TObjectData. */ + for (TClassInfoSet::iterator itr = unloadedList.begin(); + itr != unloadedList.end(); itr++) { + free((*itr)->className); + free(*itr); } - /* Release class container's spin lock. */ - spinLockRelease(&lockval); } /*! @@ -965,74 +896,57 @@ } /*! - * \brief Commit class information changing in class container.<br> - * This function is for avoiding trouble with class map.<br> - * At "afterTakeSnapShot", map is copied as shadow copy.<br> - * So crash JVM, - * if we remove item and output map at the same times. + * \brief Class unload event. Unloaded class will be added to unloaded list. + * \param jvmti [in] JVMTI environment object. + * \param env [in] JNI environment object. + * \param thread [in] Java thread object. + * \param klass [in] Unload class object. + * \sa from: hotspot/src/share/vm/prims/jvmti.xml */ -void TClassContainer::commitClassChange(void) { - TClassInfoQueue *list = NULL; +void JNICALL + OnClassUnload(jvmtiEnv *jvmti, JNIEnv *env, jthread thread, jclass klass) { + /* Get klassOop. */ + void *mirror = *(void **)klass; + void *klassOop = TVMFunctions::getInstance()->AsKlassOop(mirror); - /* Get class container's spin lock. */ - spinLockWait(&lockval); - { - /* Remove unloaded class which detected at "pushNewClass". */ - while (!unloadedList->empty()) { - TObjectData *target = unloadedList->front(); - unloadedList->pop(); + if (likely(klassOop != NULL)) { + /* Search class. */ + TObjectData *counter = clsContainer->findClass(klassOop); + if (likely(counter != NULL)) { + /* + * This function will be called at safepoint and be called by VMThread + * because class unloading is single-threaded process. + * So we can lock-free access. + */ + unloadedList.insert(counter); + } + } +} - /* Free allocated memory. */ - free(target->className); - free(target); +/*! + * \brief GarbageCollectionFinish JVMTI event to release memory for unloaded + * TObjectData. + * This function will be called at safepoint. + * All GC worker and JVMTI agent threads for HeapStats will not work + * at this point. + * So we can lock-free access. + */ +void JNICALL OnGarbageCollectionFinishForUnload(jvmtiEnv *jvmti) { + if (!unloadedList.empty()) { + /* Remove targets from snapshot container. */ + TSnapShotContainer::removeObjectDataFromAllSnapShots(unloadedList); + + /* Remove targets from class container. */ + for (TClassInfoSet::iterator itr = unloadedList.begin(); + itr != unloadedList.end(); itr++) { + TObjectData *objData = *itr; + clsContainer->removeClass(objData); + free(objData->className); + free(objData); } - try { - list = new TClassInfoQueue(); - - /* Search delete target. */ - for (TClassMap::iterator cur = classMap->begin(); cur != classMap->end(); - ++cur) { - TObjectData *objData = (*cur).second; + /* Clear unloaded list. */ + unloadedList.clear(); + } +} - /* If class is prepared remove from class container. */ - if (unlikely(objData->isRemoved && - (atomic_get(&objData->numRefs) == 0))) { - /* - * If we do removing map item here, - * iterator's relationship will be broken. - * So we store to list. And we remove after iterator loop. - */ - list->push(objData); - } - } - } catch (...) { - /* - * Maybe failed to allocate memory. - * E.g. raise exception at "new", "std::queue<T>::push" or etc.. - */ - delete list; - list = NULL; - } - - if (likely(list != NULL)) { - /* Remove delete target. */ - while (!list->empty()) { - TObjectData *target = list->front(); - list->pop(); - - /* Remove from all containers. */ - removeClass(target); - - /* Free allocated memory. */ - free(target->className); - free(target); - } - } - } - /* Release class container's spin lock. */ - spinLockRelease(&lockval); - - /* Cleanup. */ - delete list; -}
--- a/agent/src/heapstats-engines/classContainer.hpp Thu Feb 09 22:04:01 2017 +0900 +++ b/agent/src/heapstats-engines/classContainer.hpp Mon Feb 27 19:22:39 2017 +0900 @@ -67,11 +67,6 @@ typedef std::deque<TClassContainer *> TLocalClassContainer; /*! - * \brief This type is for storing unloaded class information. - */ -typedef std::queue<TObjectData *> TClassInfoQueue; - -/*! * \brief This class is stored class information.<br> * e.g. class-name, class instance count, size, etc... */ @@ -106,12 +101,6 @@ virtual TObjectData *pushNewClass(void *klassOop, TObjectData *objData); /*! - * \brief Mark class in container to remove class. - * \param target [in] Remove class data. - */ - virtual void popClass(TObjectData *target); - - /*! * \brief Remove class from container. * \param target [in] Remove class data. */ @@ -258,15 +247,6 @@ return result; } - /*! - * \brief Commit class information changing in class container.<br> - * This function needs to prevent the crash which is related - * to class unloading. <br> - * Agent have to keep ObjectData structure(s) until dumping - * SnapShot and showing heap ranking. - */ - virtual void commitClassChange(void); - protected: /*! * \brief ClassContainer in TLS of each threads. @@ -303,11 +283,26 @@ * \brief Do we need to clear at destructor? */ bool needToClear; - - /*! - * \brief List of class information which detected unloading. - */ - TClassInfoQueue *unloadedList; }; +/*! + * \brief Class unload event. Unloaded class will be added to unloaded list. + * \param jvmti [in] JVMTI environment object. + * \param env [in] JNI environment object. + * \param thread [in] Java thread object. + * \param klass [in] Unload class object. + * \sa from: hotspot/src/share/vm/prims/jvmti.xml + */ +void JNICALL + OnClassUnload(jvmtiEnv *jvmti, JNIEnv *env, jthread thread, jclass klass); + +/*! + * \brief GarbageCollectionFinish JVMTI event to release memory for unloaded + * TObjectData. + * This function will be called at safepoint. + * All GC worker and JVMTI agent threads for HeapStats will not work + * at this point. + */ +void JNICALL OnGarbageCollectionFinishForUnload(jvmtiEnv *jvmti); + #endif // CLASS_CONTAINER_HPP
--- a/agent/src/heapstats-engines/libmain.cpp Thu Feb 09 22:04:01 2017 +0900 +++ b/agent/src/heapstats-engines/libmain.cpp Mon Feb 27 19:22:39 2017 +0900 @@ -435,6 +435,10 @@ /* Set capabilities for Thread Recording. */ TThreadRecorder::setCapabilities(&capabilities); + /* Setup GarbageCollectionFinish callback for class unloading. */ + TGarbageCollectionFinishCallback::registerCallback( + &OnGarbageCollectionFinishForUnload); + /* Setup ClassPrepare event. */ TClassPrepareCallback::mergeCapabilities(&capabilities); TClassPrepareCallback::registerCallback(&OnClassPrepare);
--- a/agent/src/heapstats-engines/snapShotContainer.cpp Thu Feb 09 22:04:01 2017 +0900 +++ b/agent/src/heapstats-engines/snapShotContainer.cpp Mon Feb 27 19:22:39 2017 +0900 @@ -40,6 +40,11 @@ TSnapShotQueue *TSnapShotContainer::stockQueue = NULL; /*! + * \brief Set of active TSnapShotContainer set + */ +TActiveSnapShots TSnapShotContainer::activeSnapShots; + +/*! * \brief Initialize snapshot caontainer class. * \return Is process succeed. * \warning Please call only once from main thread. @@ -92,19 +97,20 @@ result = stockQueue->front(); stockQueue->pop(); } + + /* If need create new instance. */ + if (result == NULL) { + /* Create new snapshot container instance. */ + try { + result = new TSnapShotContainer(); + activeSnapShots.insert(result); + } catch (...) { + result = NULL; + } + } } EXIT_PTHREAD_SECTION(&instanceLocker) - /* If need create new instance. */ - if (result == NULL) { - /* Create new snapshot container instance. */ - try { - result = new TSnapShotContainer(); - } catch (...) { - result = NULL; - } - } - return result; } @@ -150,10 +156,15 @@ EXIT_PTHREAD_SECTION(&instanceLocker) } - if (unlikely(!existStockSpace)) { - /* Deallocate instance. */ - delete instance; + ENTER_PTHREAD_SECTION(&instanceLocker) + { + if (unlikely(!existStockSpace)) { + /* Deallocate instance. */ + activeSnapShots.erase(instance); + delete instance; + } } + EXIT_PTHREAD_SECTION(&instanceLocker) } /*! @@ -205,7 +216,6 @@ counter = counter->next; /* Deallocate TChildClassCounter. */ - atomic_inc(&aCounter->objData->numRefs, -1); free(aCounter->counter); free(aCounter); } @@ -300,7 +310,6 @@ return NULL; } - atomic_inc(&objData->numRefs, 1); this->clearObjectCounter(newCounter->counter); newCounter->objData = objData; @@ -470,68 +479,24 @@ /* Loop each children class. */ TChildClassCounter *counter = srcClsCounter->child; - TChildClassCounter *prevCounter = NULL; while (counter != NULL) { TObjectData *objData = counter->objData; - /* - * If the class of objData is already unloaded, we should free and - * shold decrement reference count. - * TChildClassCounter reference count will be checked at - * TClassContainer::commitClassChange(). - */ - if (objData->isRemoved) { - atomic_inc(&objData->numRefs, -1); - TChildClassCounter *nextCounter = counter->next; - - if (prevCounter == NULL) { - srcClsCounter->child = nextCounter; - } else { - prevCounter->next = nextCounter; - } - - /* Deallocate TChildClassCounter. */ - free(counter->counter); - free(counter); - - /* Deallocate TChildClassCounter in parent container. */ - TChildClassCounter *childClsData, *parentPrevData, - *parentMorePrevData; - this->findChildCounters(clsCounter, objData->klassOop, - &childClsData, &parentPrevData, - &parentMorePrevData); - if (likely(childClsData != NULL)) { - atomic_inc(&objData->numRefs, -1); - - if (parentPrevData == NULL) { - clsCounter->child = childClsData->next; - } else { - parentPrevData->next = childClsData->next; - } - - free(childClsData->counter); - free(childClsData); - } - - counter = nextCounter; - } else { - /* Search child class. */ - TChildClassCounter *childClsData = + /* Search child class. */ + TChildClassCounter *childClsData = this->findChildClass(clsCounter, objData->klassOop); - /* Register class as child class. */ - if (unlikely(childClsData == NULL)) { - childClsData = this->pushNewChildClass(clsCounter, objData); - } + /* Register class as child class. */ + if (unlikely(childClsData == NULL)) { + childClsData = this->pushNewChildClass(clsCounter, objData); + } - if (likely(childClsData != NULL)) { - /* Marge children class heap usage. */ - this->addInc(childClsData->counter, counter->counter); - } + if (likely(childClsData != NULL)) { + /* Marge children class heap usage. */ + this->addInc(childClsData->counter, counter->counter); + } - prevCounter = counter; - counter = counter->next; - } + counter = counter->next; } } } @@ -539,3 +504,81 @@ /* Release snapshot container's spin lock. */ spinLockRelease(&lockval); } + +/*! + * \brief Remove unloaded TObjectData in this snapshot container. + * This function should be called at safepoint. + * \param unloadedList Set of unloaded TObjectData. + */ +void TSnapShotContainer::removeObjectData(TClassInfoSet &unloadedList) { + TSizeMap::iterator itr; + + /* Remove the target from parent container. */ + for (TClassInfoSet::iterator target = unloadedList.begin(); + target != unloadedList.end(); target++) { + itr = counterMap.find(*target); + if (itr != counterMap.end()) { + TClassCounter *clsCounter = itr->second; + TChildClassCounter *childCounter = clsCounter->child; + + while (childCounter != NULL) { + TChildClassCounter *nextCounter = childCounter->next; + free(childCounter->counter); + free(childCounter); + childCounter = nextCounter; + } + + free(clsCounter->counter); + free(clsCounter); + counterMap.erase(itr); + } + } + + /* Remove the target from all children in counterMap. */ + for (itr = counterMap.begin(); itr != counterMap.end(); itr++) { + TClassCounter *clsCounter = itr->second; + TChildClassCounter *childCounter = clsCounter->child; + TChildClassCounter *prevChildCounter = NULL; + + while (childCounter != NULL) { + TChildClassCounter *nextCounter = childCounter->next; + + if (unloadedList.find(childCounter->objData) != unloadedList.end()) { + free(childCounter->counter); + free(childCounter); + if (prevChildCounter == NULL) { + clsCounter->child = nextCounter; + } else { + prevChildCounter->next = nextCounter; + } + } else { + prevChildCounter = childCounter; + } + + childCounter = nextCounter; + } + } + + /* Remove the target from local containers. */ + for (TLocalSnapShotContainer::iterator container = containerMap.begin(); + container != containerMap.end(); container++) { + container->second->removeObjectData(unloadedList); + } +} + +/*! + * \brief Remove unloaded TObjectData all active snapshot container. + * \param unloadedList Set of unloaded TObjectData. + */ +void TSnapShotContainer::removeObjectDataFromAllSnapShots( + TClassInfoSet &unloadedList) { + ENTER_PTHREAD_SECTION(&instanceLocker) + { + for (TActiveSnapShots::iterator itr = activeSnapShots.begin(); + itr != activeSnapShots.end(); itr++) { + (*itr)->removeObjectData(unloadedList); + } + } + EXIT_PTHREAD_SECTION(&instanceLocker) +} +
--- a/agent/src/heapstats-engines/snapShotContainer.hpp Thu Feb 09 22:04:01 2017 +0900 +++ b/agent/src/heapstats-engines/snapShotContainer.hpp Mon Feb 27 19:22:39 2017 +0900 @@ -25,6 +25,7 @@ #include <pthread.h> #include <tr1/unordered_map> +#include <tr1/unordered_set> #include <queue> #include "jvmInfo.hpp" @@ -67,21 +68,23 @@ /*! * \brief This structure stored class information. */ -#pragma pack(push, 4) typedef struct { - jlong tag; /*!< Class tag. */ - jlong classNameLen; /*!< Class name. */ - char *className; /*!< Class name length. */ - void *klassOop; /*!< Java inner class object. */ - jlong oldTotalSize; /*!< Class old total use size. */ - TOopType oopType; /*!< Type of class. */ - jlong clsLoaderId; /*!< Class loader instance id. */ - jlong clsLoaderTag; /*!< Class loader class tag. */ - bool isRemoved; /*!< Class is already unloaded. */ - jlong instanceSize; /*!< Class size if this class is instanceKlass. */ - int numRefs; /*!< Number of references. */ + jlong tag; /*!< Class tag. */ + jlong classNameLen; /*!< Class name. */ + char *className; /*!< Class name length. */ + void *klassOop; /*!< Java inner class object. */ + jlong oldTotalSize; /*!< Class old total use size. */ + TOopType oopType; /*!< Type of class. */ + jlong clsLoaderId; /*!< Class loader instance id. */ + jlong clsLoaderTag; /*!< Class loader class tag. */ + jlong instanceSize; /*!< Class size if this class is instanceKlass. */ } TObjectData; -#pragma pack(pop) + +/*! + * \brief This type is for storing unloaded class information. + */ +typedef std::tr1::unordered_set<TObjectData *, + TNumericalHasher<void *> > TClassInfoSet; /*! * \brief This structure stored child class size information. @@ -155,6 +158,10 @@ TNumericalHasher<pthread_t> > TLocalSnapShotContainer; +typedef std::tr1::unordered_set<TSnapShotContainer *, + TNumericalHasher<void *> > TActiveSnapShots; + + /*! * \brief This class is stored class object usage on heap. */ @@ -263,38 +270,6 @@ } /*! - * \brief Find child class and its prevous data. - * \param clsCounter [in] Parent class counter object. - * \param klassOop [in] Child class key object. - * \param counter [out] Child data - * \param prevCounter [out] previous counter of `counter` - * \param morePrevCounter [out] previous counter of `prevCounter` - */ - inline void findChildCounters(TClassCounter *clsCounter, void *klassOop, - TChildClassCounter **counter, - TChildClassCounter** prevCounter, - TChildClassCounter **morePrevCounter) { - *prevCounter = NULL; - *morePrevCounter = NULL; - *counter = clsCounter->child; - - if (*counter == NULL) { - return; - } - - /* Search children class list. */ - while ((*counter)->objData->klassOop != klassOop) { - *morePrevCounter = *prevCounter; - *prevCounter = *counter; - *counter = (*counter)->next; - - if (*counter == NULL) { - return; - } - } - }; - - /*! * \brief Find child class data. * \param clsCounter [in] Parent class counter object. * \param klassOop [in] Child class key object. @@ -307,13 +282,21 @@ TChildClassCounter *morePrevCounter = NULL; TChildClassCounter *counter = clsCounter->child; - this->findChildCounters(clsCounter, klassOop, - &counter, &prevCounter, &morePrevCounter); - if (counter == NULL) { return NULL; } + /* Search children class list. */ + while (counter->objData->klassOop != klassOop) { + morePrevCounter = prevCounter; + prevCounter = counter; + counter = counter->next; + + if (counter == NULL) { + return NULL; + } + } + /* LFU (Least Frequently Used). */ if (counter != NULL) { counter->callCount++; @@ -424,6 +407,19 @@ */ inline void setIsCleared(bool flag) { this->isCleared = flag; } + /*! + * \brief Remove unloaded TObjectData in this snapshot container. + * This function should be called at safepoint. + * \param unloadedList Set of unloaded TObjectData. + */ + void removeObjectData(TClassInfoSet &unloadedList); + + /*! + * \brief Remove unloaded TObjectData all active snapshot container. + * \param unloadedList Set of unloaded TObjectData. + */ + static void removeObjectDataFromAllSnapShots(TClassInfoSet &unloadedList); + protected: /*! * \brief TSnapshotContainer constructor. @@ -509,6 +505,11 @@ * \brief Is this container is cleared ? */ volatile bool isCleared; + + /*! + * \brief Set of active TSnapShotContainer set + */ + static TActiveSnapShots activeSnapShots; }; /* Include optimized inline functions. */
--- a/agent/src/heapstats-engines/snapShotMain.cpp Thu Feb 09 22:04:01 2017 +0900 +++ b/agent/src/heapstats-engines/snapShotMain.cpp Mon Feb 27 19:22:39 2017 +0900 @@ -146,31 +146,6 @@ } /*! - * \brief Class unload event. - * \param jvmti [in] JVMTI environment object. - * \param env [in] JNI environment object. - * \param thread [in] Java thread object. - * \param klass [in] Unload class object. - * \sa from: hotspot/src/share/vm/prims/jvmti.xml - */ -void JNICALL - OnClassUnload(jvmtiEnv *jvmti, JNIEnv *env, jthread thread, jclass klass) { - /* Get klassOop. */ - void *mirror = *(void **)klass; - void *klassOop = TVMFunctions::getInstance()->AsKlassOop(mirror); - - if (likely(klassOop != NULL)) { - /* Search class. */ - TObjectData *counter = clsContainer->findClass(klassOop); - - if (likely(counter != NULL)) { - /* Remove class data. */ - clsContainer->popClass(counter); - } - } -} - -/*! * \brief Setting JVM information to snapshot. * \param snapshot [in] Snapshot instance. * \param cause [in] Cause of taking a snapshot.<br> @@ -378,12 +353,7 @@ TObjectData *clsData = getObjectDataFromKlassOop(localClsContainer, klassOop); /* Push new child loaded class. */ - if (!clsData->isRemoved) { - clsCounter = localSnapshot->pushNewChildClass(parentCounter, clsData); - } else { - /* We should return if clsData has already been removed (unloaded). */ - return; - } + clsCounter = localSnapshot->pushNewChildClass(parentCounter, clsData); } if (unlikely(clsCounter == NULL)) {
--- a/agent/src/heapstats-engines/snapShotMain.hpp Thu Feb 09 22:04:01 2017 +0900 +++ b/agent/src/heapstats-engines/snapShotMain.hpp Mon Feb 27 19:22:39 2017 +0900 @@ -102,17 +102,6 @@ OnClassPrepare(jvmtiEnv *jvmti, JNIEnv *env, jthread thread, jclass klass); /*! - * \brief Class unload event. - * \param jvmti [in] JVMTI environment object. - * \param env [in] JNI environment object. - * \param thread [in] Java thread object. - * \param klass [in] Unload class object. - * \sa from: hotspot/src/share/vm/prims/jvmti.xml - */ -void JNICALL - OnClassUnload(jvmtiEnv *jvmti, JNIEnv *env, jthread thread, jclass klass); - -/*! * \brief Before garbage collection event. * \param jvmti [in] JVMTI environment object. */
--- a/agent/src/heapstats-engines/snapShotProcessor.cpp Thu Feb 09 22:04:01 2017 +0900 +++ b/agent/src/heapstats-engines/snapShotProcessor.cpp Mon Feb 27 19:22:39 2017 +0900 @@ -1,7 +1,7 @@ /*! * \file snapShotProcessor.cpp * \brief This file is used to output ranking and call snapshot function. - * Copyright (C) 2011-2015 Nippon Telegraph and Telephone Corporation + * Copyright (C) 2011-2017 Nippon Telegraph and Telephone Corporation * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -127,7 +127,6 @@ } /* Clean up. */ - controller->_container->commitClassChange(); TSnapShotContainer::releaseInstance(snapshot); delete ranking; ranking = NULL;
--- a/agent/src/heapstats-engines/util.hpp Thu Feb 09 22:04:01 2017 +0900 +++ b/agent/src/heapstats-engines/util.hpp Mon Feb 27 19:22:39 2017 +0900 @@ -368,14 +368,6 @@ }; /* CPU Specific utilities. */ -#if PROCESSOR_ARCH == X86 -#include "arch/x86/util.inline.hpp" -#elif PROCESSOR_ARCH == ARM -#include "arch/arm/util.inline.hpp" -#else -#error "Unknown CPU architecture." -#endif - #ifdef AVX #include "arch/x86/avx/util.hpp" #elif defined(SSE2) || defined(SSE3) || defined(SSE4)