changeset 66:bbf1ae6d2a2e

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:25:26 +0900
parents ef928edf1d20
children 2518d4eee904
files agent/src/classContainer.cpp agent/src/classContainer.hpp agent/src/snapShotContainer.cpp agent/src/snapShotContainer.hpp agent/src/snapShotMain.cpp agent/src/snapShotMain.hpp agent/src/snapShotProcessor.cpp agent/src/util.hpp
diffstat 8 files changed, 263 insertions(+), 379 deletions(-) [+]
line wrap: on
line diff
--- a/agent/src/classContainer.cpp	Thu Feb 09 12:31:27 2017 +0900
+++ b/agent/src/classContainer.cpp	Mon Feb 27 19:25:26 2017 +0900
@@ -94,6 +94,11 @@
  */
 static char ORDER_DELTA[6] = "DELTA";
 
+static TClassInfoSet unloadedList;
+volatile int unloadedList_lock = 0;
+
+extern TClassContainer *clsContainer;
+
 /*!
  * \brief TClassContainer constructor.
  * \param base      [in] Parent class container instance.
@@ -108,7 +113,6 @@
     needToClear = needToClr;
     classMap     = NULL;
     pSender      = NULL;
-    unloadedList = NULL;
     
     if (likely(base != NULL)) {
         
@@ -144,9 +148,6 @@
         pSender = new TTrapSender(SNMP_VERSION_2c, arg.snmpTarget,
             arg.snmpComName, 162);
         
-        /* Create unloaded class information queue. */
-        unloadedList = new TClassInfoQueue();
-        
         /* Create thread storage key. */
         if (unlikely(pthread_key_create(&clsContainerKey, NULL) != 0)) {
             throw 1;
@@ -154,7 +155,6 @@
     } catch(...) {
         delete classMap;
         delete pSender;
-        delete unloadedList;
         throw "TClassContainer initialize failed!";
     }
 }
@@ -179,7 +179,6 @@
     /* Cleanup instances. */
     delete classMap;
     delete pSender;
-    delete unloadedList;
     
     /* Cleanup thread storage key. */
     pthread_key_delete(clsContainerKey);
@@ -243,7 +242,6 @@
         free(cur);
     }
 
-    atomic_inc(&result->numRefs, 1);
     return result;
 }
 
@@ -281,23 +279,14 @@
                     && 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);
                 }
             }
         }
@@ -327,10 +316,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. */
@@ -339,19 +325,6 @@
 }
 
 /*!
- * \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.
  */
@@ -359,7 +332,6 @@
     
     /* 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);
@@ -367,17 +339,11 @@
         /* 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);
-            }
+          /* Get local container's spin lock. */
+          spinLockWait(&(*it)->lockval);
+          { (*it)->classMap->erase(target->klassOop); }
+          /* Release local container's spin lock. */
+          spinLockRelease(&(*it)->lockval);
         }
     }
     /* Release spin lock of containers queue. */
@@ -386,61 +352,22 @@
 
 /*!
  * \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) {
-    
-    /* 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();
-            }
-            /* 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);
+  /* Add all TObjectData pointers in parent container map to unloadedList */
+  for (TClassMap::iterator cur = classMap->begin(); cur != classMap->end();
+       ++cur) {
+    unloadedList.insert(cur->second);
+  }
 
-              if (atomic_get(&pos->numRefs) == 0) {
-                free(pos->className);
-                free(pos);
-              }
-            }
-        }
-        
-        /* 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 class container's spin lock. */
-    spinLockRelease(&lockval);
+  /* Release all memory for TObjectData. */
+  for (TClassInfoSet::iterator itr = unloadedList.begin();
+       itr != unloadedList.end(); itr++) {
+    free((*itr)->className);
+    free(*itr);
+  }
 }
 
 /*!
@@ -1021,77 +948,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;
-    
-    /* Get class container's spin lock. */
-    spinLockWait(&lockval);
-    {
-        /* Remove unloaded class which detected at "pushNewClass". */
-        while (!unloadedList->empty()) {
-            TObjectData *target = unloadedList->front();
-            unloadedList->pop();
-            
-            /* Free allocated memory. */
-            free(target->className);
-            free(target);
-        }
-        
-        try {
-            list = new TClassInfoQueue();
-            
-            /* Search delete target. */
-            for (TClassMap::iterator cur = classMap->begin();
-                cur != classMap->end(); ++cur) {
-                TObjectData *objData = (*cur).second;
-                
-                /* 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);
-            }
-        }
+void JNICALL
+    OnClassUnload(jvmtiEnv *jvmti, JNIEnv *env, jthread thread, jclass klass) {
+  /* Get klassOop. */
+  void *mirror = *(void **)klass;
+  void *klassOop = asKlassOop(mirror);
+
+  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);
     }
-    /* Release class container's spin lock. */
-    spinLockRelease(&lockval);
-    
-    /* Cleanup. */
-    delete list;
+  }
 }
 
+/*!
+ * \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);
+    }
+
+    /* Clear unloaded list. */
+    unloadedList.clear();
+  }
+}
+
--- a/agent/src/classContainer.hpp	Thu Feb 09 12:31:27 2017 +0900
+++ b/agent/src/classContainer.hpp	Mon Feb 27 19:25:26 2017 +0900
@@ -71,11 +71,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...
  */
@@ -110,12 +105,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.
          */
@@ -268,15 +257,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.
@@ -313,11 +293,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/snapShotContainer.cpp	Thu Feb 09 12:31:27 2017 +0900
+++ b/agent/src/snapShotContainer.cpp	Mon Feb 27 19:25:26 2017 +0900
@@ -50,6 +50,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.
@@ -100,25 +105,23 @@
     TSnapShotContainer *result = NULL;
     
     ENTER_PTHREAD_SECTION(&instanceLocker) {
-        
-        if (!stockQueue->empty()) {
-            
-            /* Reuse snapshot container instance. */
-            result = stockQueue->front();
-            stockQueue->pop();
-        }
-    } EXIT_PTHREAD_SECTION(&instanceLocker)
-    
-    /* If need create new instance. */
-    if (result == NULL) {
-        
+      if (!stockQueue->empty()) {
+        /* Reuse snapshot container instance. */
+        result = stockQueue->front();
+        stockQueue->pop();
+      }
+      /* If need create new instance. */
+      if (result == NULL) {
         /* Create new snapshot container instance. */
         try {
-            result = new TSnapShotContainer();
-        } catch(...) {
-            result = NULL;
+          result = new TSnapShotContainer();
+          activeSnapShots.insert(result);
+        } catch (...) {
+          result = NULL;
         }
+      }
     }
+    EXIT_PTHREAD_SECTION(&instanceLocker)
     
     return result;
 }
@@ -164,11 +167,15 @@
         } EXIT_PTHREAD_SECTION(&instanceLocker)
     }
     
+  ENTER_PTHREAD_SECTION(&instanceLocker)
+  {
     if (unlikely(!existStockSpace)) {
-        
-        /* Deallocate instance. */
-        delete instance;
+      /* Deallocate instance. */
+      activeSnapShots.erase(instance);
+      delete instance;
     }
+  }
+  EXIT_PTHREAD_SECTION(&instanceLocker)
 }
 
 /*!
@@ -222,7 +229,6 @@
             counter = counter->next;
             
             /* Deallocate TChildClassCounter. */
-            atomic_inc(&aCounter->objData->numRefs, -1);
             free(aCounter->counter);
             free(aCounter);
         }
@@ -350,7 +356,6 @@
             : : "r" (newCounter->counter) : "cc", "memory", "%xmm0");
     }
 
-    atomic_inc(&objData->numRefs, 1);
     newCounter->objData = objData;
     
     /* Chain children list. */
@@ -595,70 +600,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);
-                    }
-
-                    if (likely(childClsData != NULL)) {
-                      /* Marge children class heap usage. */
-                      this->addInc(childClsData->counter, counter->counter);
-                    }
-
-                    prevCounter = counter;
-                    counter = counter->next;
+                  /* 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);
+                  }
+
+                  counter = counter->next;
                 }
 
             }
@@ -667,3 +626,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/snapShotContainer.hpp	Thu Feb 09 12:31:27 2017 +0900
+++ b/agent/src/snapShotContainer.hpp	Mon Feb 27 19:25:26 2017 +0900
@@ -28,6 +28,7 @@
 #include <jni.h>
 #include <pthread.h>
 #include <tr1/unordered_map>
+#include <tr1/unordered_set>
 #include <queue>
 
 #include "util.hpp"
@@ -84,12 +85,17 @@
     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.                      */
 } TObjectData;
 
 /*!
+ * \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.
  */
 struct TChildClassCounter{
@@ -159,6 +165,9 @@
 typedef std::tr1::unordered_map<pthread_t, TSnapShotContainer*,
     TNumericalHasher<pthread_t> > TLocalSnapShotContainer;
 
+typedef std::tr1::unordered_set<TSnapShotContainer *,
+                                TNumericalHasher<void *> > TActiveSnapShots;
+
 /*!
  * \brief This class is stored class object usage on heap.
  */
@@ -322,38 +331,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.
@@ -366,13 +343,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++;
@@ -492,6 +477,20 @@
           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.
@@ -560,6 +559,10 @@
          */
         volatile bool isCleared;
 
+        /*!
+         * \brief Set of active TSnapShotContainer set
+         */
+        static TActiveSnapShots activeSnapShots;
 };
 
 #endif // _SNAPSHOT_CONTAINER_HPP
--- a/agent/src/snapShotMain.cpp	Thu Feb 09 12:31:27 2017 +0900
+++ b/agent/src/snapShotMain.cpp	Mon Feb 27 19:25:26 2017 +0900
@@ -163,33 +163,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 = 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>
@@ -320,6 +293,8 @@
  * \param jvmti [in] JVMTI environment object.
  */
 void JNICALL OnGarbageCollectionFinish(jvmtiEnv *jvmti) {
+  /* Process unloaded classes. */
+  OnGarbageCollectionFinishForUnload(jvmti);
     
   /* Disable inner GC event. */
   setupHookForInnerGCEvent(false, NULL);
@@ -340,6 +315,9 @@
  * \brief After G1 garbage collection event.
  */
 void OnG1GarbageCollectionFinish(void) {
+  /* Process unloaded classes. */
+  OnGarbageCollectionFinishForUnload(NULL);
+
   //jvmInfo->loadGCCause();
   jvmInfo->SetUnknownGCCause();
 
@@ -405,12 +383,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)){
@@ -618,7 +591,9 @@
  * \param jvmti [in] JVMTI environment object.
  */
 void JNICALL OnCMSGCFinish(jvmtiEnv *jvmti) {
-    
+    /* Process unloaded classes. */
+    OnGarbageCollectionFinishForUnload(jvmti);
+
     /* Disable inner GC event. */
     setupHookForInnerGCEvent(false, NULL);
     
--- a/agent/src/snapShotMain.hpp	Thu Feb 09 12:31:27 2017 +0900
+++ b/agent/src/snapShotMain.hpp	Mon Feb 27 19:25:26 2017 +0900
@@ -1,7 +1,7 @@
 /*!
  * \file snapShotMain.hpp
  * \brief This file is used to take snapshot.
- * Copyright (C) 2011-2013 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
@@ -109,17 +109,6 @@
     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/snapShotProcessor.cpp	Thu Feb 09 12:31:27 2017 +0900
+++ b/agent/src/snapShotProcessor.cpp	Mon Feb 27 19:25:26 2017 +0900
@@ -1,7 +1,7 @@
 /*!
  * \file snapShotProcessor.cpp
  * \brief This file is used to output ranking and call snapshot function.
- * Copyright (C) 2011-2013 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
@@ -146,7 +146,6 @@
             }
             
             /* Clean up. */
-            controller->_container->commitClassChange();
             TSnapShotContainer::releaseInstance(snapshot);
             delete ranking;
             ranking = NULL;
--- a/agent/src/util.hpp	Thu Feb 09 12:31:27 2017 +0900
+++ b/agent/src/util.hpp	Mon Feb 27 19:25:26 2017 +0900
@@ -1,7 +1,7 @@
 /*!
  * \file util.hpp
  * \brief This file is utilities.
- * Copyright (C) 2011-2014 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
@@ -588,25 +588,4 @@
         };
 };
 
-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