Snap for 4765094 from ded96ac6cd0e77ee864e829d9d3127283228dc5f to pi-release
Change-Id: Ifefb826040a3d2afb194c4f17a1ec9e36b59d424
diff --git a/src/share/back/classTrack.c b/src/share/back/classTrack.c
index 1db2f47..aea139a 100644
--- a/src/share/back/classTrack.c
+++ b/src/share/back/classTrack.c
@@ -42,23 +42,28 @@
* associated with the tag we gave to that class. The tag is
* simply incremented every time we add a new class.
*
- * When we compare with the previous set of classes we iterate
- * through the list and remove any nodes which have no objects
- * associated with their tag, reporting these as the classes
- * that have been unloaded.
+ * We also request (on the separate tracking jvmtiEnv) an
+ * ObjectFree event be called for each of these classes. This
+ * allows us to keep a running list of all the classes known to
+ * have been collected since the last call to
+ * classTrack_processUnloads. On each call to processUnloads we
+ * iterate through this list and remove from the main list all
+ * the objects that have been collected. We then return a list of
+ * the class-signatures that have been collected.
*
* For efficiency and simplicity we don't bother retagging or
* re-using old tags, instead relying on the fact that no
* program will ever be able to exhaust the (2^64 - 1) possible
* tag values (which would require that many class-loads).
*
- * This relies on the tagging implementation being relatively
- * efficient for performance. It has the advantage of not
- * requiring any jweaks.
+ * This relies on the tagging and ObjectFree implementation being
+ * relatively efficient for performance. It has the advantage of
+ * not requiring any jweaks.
*
* All calls into any function of this module must be either
* done before the event-handler system is setup or done while
- * holding the event handlerLock.
+ * holding the event handlerLock. The list of freed classes is
+ * protected by the classTagLock.
*/
#include "util.h"
@@ -89,57 +94,47 @@
static jlong currentKlassTag;
/*
- * Delete a linked-list of classes.
- * The signatures of classes in the table are returned.
+ * A lock to protect access to 'deletedTagBag'
*/
-static struct bag *
-deleteList(KlassNode *node)
+static jrawMonitorID deletedTagLock;
+
+/*
+ * A bag containing all the deleted klass_tags ids. This must be accessed under the
+ * deletedTagLock.
+ *
+ * It is cleared each time classTrack_processUnloads is called.
+ */
+struct bag* deletedTagBag;
+
+/*
+ * The callback for when classes are freed. Only classes are called because this is registered with
+ * the trackingEnv which only tags classes.
+ */
+static void JNICALL
+cbTrackingObjectFree(jvmtiEnv* jvmti_env, jlong tag)
{
- struct bag *signatures = bagCreateBag(sizeof(char*), 10);
- jint slot;
-
- if (signatures == NULL) {
- EXIT_ERROR(AGENT_ERROR_OUT_OF_MEMORY,"signatures");
- }
-
- while (node != NULL) {
- KlassNode *next;
- char **sigSpot;
-
- /* Add signature to the signature bag */
- sigSpot = bagAdd(signatures);
- if (sigSpot == NULL) {
- EXIT_ERROR(AGENT_ERROR_OUT_OF_MEMORY,"signature bag");
- }
- *sigSpot = node->signature;
-
- /* No need to delete the tag since the object was already destroyed. */
- next = node->next;
- jvmtiDeallocate(node);
-
- node = next;
- }
-
- return signatures;
+ debugMonitorEnter(deletedTagLock);
+ *(jlong*)bagAdd(deletedTagBag) = tag;
+ debugMonitorExit(deletedTagLock);
}
+/*
+ * Returns true (thus continuing the iteration) if the item is not the searched for tag.
+ */
static jboolean
-isClassUnloaded(jlong tag) {
- jvmtiError error;
- jint res_count;
- error = JVMTI_FUNC_PTR(trackingEnv,GetObjectsWithTags)(trackingEnv,
- /*tag_count*/ 1,
- &tag,
- &res_count,
- /*object_result_ptr*/ NULL,
- /*tag_result_ptr*/ NULL);
- if (error != JVMTI_ERROR_NONE) {
- EXIT_ERROR(error,"Failed GetObjectsWithTags for class tracking");
- }
- if (res_count != 0 && res_count != 1) {
- EXIT_ERROR(AGENT_ERROR_INTERNAL,"Unexpected extra tags in trackingEnv!");
- }
- return res_count == 0 ? JNI_TRUE : JNI_FALSE;
+isNotTag(void* item, void* needle)
+{
+ return *(jlong*)item != *(jlong*)needle;
+}
+
+/*
+ * This requires that deletedTagLock and the handlerLock are both held.
+ */
+static jboolean
+isClassUnloaded(jlong tag)
+{
+ /* bagEnumerateOver returns true if 'func' returns true on all items and aborts early if not. */
+ return !bagEnumerateOver(deletedTagBag, isNotTag, &tag);
}
/*
@@ -156,31 +151,48 @@
struct bag *
classTrack_processUnloads(JNIEnv *env)
{
- KlassNode *toDeleteList = NULL;
- jboolean anyRemoved = JNI_FALSE;
- KlassNode* node = list;
- KlassNode** previousNext = &list;
- /* Filter out all the unloaded classes from the list. */
- while (node != NULL) {
- if (isClassUnloaded(node->klass_tag)) {
- /* Update the previous node's next pointer to point after this node. Note that we update
- * the value pointed to by previousNext but not the value of previousNext itself.
- */
- *previousNext = node->next;
- /* Remove this node from the 'list' and put it into toDeleteList */
- node->next = toDeleteList;
- toDeleteList = node;
- anyRemoved = JNI_TRUE;
- } else {
- /* This node will become the previous node so update the previousNext pointer to this
- * nodes next pointer.
- */
- previousNext = &(node->next);
- }
- node = *previousNext;
- }
+ /* We could optimize this somewhat by holding the deletedTagLock for a much shorter time,
+ * replacing it as soon as we enter and then destroying it once we are done with it. This will
+ * cause a lot of memory churn and this function is not expected to be called that often.
+ * Furthermore due to the check for an empty bag (which should be very common) normally this
+ * will finish very quickly. In cases where there is a concurrent GC occuring and a class is
+ * being collected the GC-ing threads could be blocked until we are done but this is expected to
+ * be very rare.
+ */
+ debugMonitorEnter(deletedTagLock);
+ /* Take and return the deletedTagBag */
+ struct bag* deleted = bagCreateBag(sizeof(char*), bagSize(deletedTagBag));
+ /* The deletedTagBag is going to be much shorter than the klassNode list so we should walk the
+ * KlassNode list once and scan the deletedTagBag each time. We only need to this in the rare
+ * case that there was anything deleted though.
+ */
+ if (bagSize(deletedTagBag) != 0) {
+ KlassNode* node = list;
+ KlassNode** previousNext = &list;
- return deleteList(toDeleteList);
+ while (node != NULL) {
+ if (isClassUnloaded(node->klass_tag)) {
+ /* Update the previous node's next pointer to point after this node. Note that we
+ * update the value pointed to by previousNext but not the value of previousNext
+ * itself.
+ */
+ *previousNext = node->next;
+ /* Put this nodes signature into the deleted bag */
+ *(char**)bagAdd(deleted) = node->signature;
+ /* Deallocate the node */
+ jvmtiDeallocate(node);
+ } else {
+ /* This node will become the previous node so update the previousNext pointer to
+ * this nodes next pointer.
+ */
+ previousNext = &(node->next);
+ }
+ node = *previousNext;
+ }
+ bagDeleteAll(deletedTagBag);
+ }
+ debugMonitorExit(deletedTagLock);
+ return deleted;
}
/*
@@ -227,6 +239,31 @@
list = node;
}
+static jboolean
+setupEvents()
+{
+ jvmtiCapabilities caps;
+ memset(&caps, 0, sizeof(caps));
+ caps.can_generate_object_free_events = 1;
+ jvmtiError error = JVMTI_FUNC_PTR(trackingEnv,AddCapabilities)(trackingEnv, &caps);
+ if (error != JVMTI_ERROR_NONE) {
+ return JNI_FALSE;
+ }
+ jvmtiEventCallbacks cb;
+ memset(&cb, 0, sizeof(cb));
+ cb.ObjectFree = cbTrackingObjectFree;
+ error = JVMTI_FUNC_PTR(trackingEnv,SetEventCallbacks)(trackingEnv, &cb, sizeof(cb));
+ if (error != JVMTI_ERROR_NONE) {
+ return JNI_FALSE;
+ }
+ error = JVMTI_FUNC_PTR(trackingEnv,SetEventNotificationMode)
+ (trackingEnv, JVMTI_ENABLE, JVMTI_EVENT_OBJECT_FREE, NULL);
+ if (error != JVMTI_ERROR_NONE) {
+ return JNI_FALSE;
+ }
+ return JNI_TRUE;
+}
+
/*
* Called once to build the initial prepared class hash table.
*/
@@ -238,6 +275,15 @@
if ( trackingEnv == NULL ) {
EXIT_ERROR(AGENT_ERROR_INTERNAL,"Failed to allocate tag-tracking jvmtiEnv");
}
+ /* We want to create these before turning on the events or tagging anything. */
+ deletedTagLock = debugMonitorCreate("Deleted class tag lock");
+ deletedTagBag = bagCreateBag(sizeof(jlong), 10);
+ /* ANDROID-CHANGED: Setup the trackingEnv's ObjectFree event */
+ if (!setupEvents()) {
+ /* On android classes are usually not unloaded too often so this is not a huge loss. */
+ ERROR_MESSAGE(("Unable to setup class ObjectFree tracking! Class unloads will not "
+ "be reported!"));
+ }
currentKlassTag = 0l;
list = NULL;
WITH_LOCAL_REFS(env, 1) {