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)