very special class objects detected
Hi,
using a code near to your heap viewer demo which pre tags all classes before the first iterateOverReachableObject() is called reveales special class objects.
Using the tagging mechanism in iterateOverReachableObjects I detect 10 objects with the following attributes:
- the object reached (the referee) is untagged (the tag points to zero)
- the matching class tag points to the class "Ljava.lang.Class" (we have a class object)
- each is a root object of type 7
But as we have pre tagged all class objects initially, what class objects are those as they are still untagged? The analysis of the references shows the following references with the reference type given in the arrow (i called those special class objects "*JVMTI unknown*"):
Ljava.lang.Class(Ljava.lang.Integer) --8--> Ljava.lang.Class(*JVMTI unknown*)
Ljava.lang.Class(Ljava.lang.Double) --8--> Ljava.lang.Class(*JVMTI unknown*)
Ljava.io.ObjectStreamField --2--> Ljava.lang.Class(*JVMTI unknown*) [appeared 6 times]
Ljava.lang.Class(Ljava.lang.Boolean) --8--> Ljava.lang.Class(*JVMTI unknown*)
Ljava.lang.Class(Ljava.lang.Character) --8--> Ljava.lang.Class(*JVMTI unknown*)
Ljava.lang.Class(Ljava.lang.Byte) --8--> Ljava.lang.Class(*JVMTI unknown*)
Ljava.lang.Class(Ljava.lang.Short) --8--> Ljava.lang.Class(*JVMTI unknown*)
Ljava.lang.Class(Ljava.lang.Long) --8--> Ljava.lang.Class(*JVMTI unknown*)
Ljava.lang.Class(Ljava.lang.Float) --8--> Ljava.lang.Class(*JVMTI unknown*)
Ljava.lang.Class(*JVMTI unknown*) --1--> Ljava.lang.Class(Ljava.lang.Object)
To got that a very simple java appliaction was used with the JVMTI agent. I assume that those objects appear with every application.
USING:
java version "1.5.0_06"
Java(TM) 2 Runtime Environment, Standard Edition (build 1.5.0_06-b05)
Java HotSpot(TM) Server VM (build 1.5.0_06-b05, mixed mode)
on Solaris9/FJSV,SPARC64-IV
and
java version "1.5.0"
Java(TM) 2 Runtime Environment, Standard Edition (build 1.5.0-b64)
Java HotSpot(TM) Client VM (build 1.5.0-b64, mixed mode, sharing)
on Windows XP SP2/Pentium 4
Thanks Robert
I think these OTHER objects are placeholders in the system dictionary for primitive classes. I'd be curious if you see these when you run with 5.0u7 or Mustang (5093520 comes to mind but I would need to look further to be sure).
I am not able to install a new java version on out testing environment so I have to get along with this version seeing those class objects. If it is a bug to see those classes it would be kind if you could post me its id. Thanks Robert
An additional question: I have found 10 such classes, but we only have 8 primitive classes:
Boolean
Character
Byte
Short
Integer
Long
Float
Double
What about the remaining two? One of those two is the class which references Ljava.lang.Class(Ljava.lang.Object) with a type 1 reference.
Thanks, Robert
The easiest way to find out what these are is to tag them and then obtain a JNI reference using GetObjectsWithTags. Once you can a JNI ref you can query them (GetObjectClass for example) so that you know what you are seeing.
FWIW, the roots that are reported as "OTHER" are mostly just a small objects preloaded by the VM. Examples would be preallocated instances of OutOfMemoryError, NullPointerException and a few other important exceptions.
But this seems to result in nothing. I know that those objects are instances of java.lang.Class, and I do not think that GetObjectClass will give me more information, does it?
AND ... how to use those GetObjectClass function in my JVMTI agent. There I only have a jvmtiEnv, but no JNIEnv which is needed. An exported function named JNI_OnLoad is not executed where it would be possible to get that JNIEnv.
Thanks Robert
I am sorry but I see those class objects with mustang too. If it is a bug it has not been fixed with
java version "1.6.0-beta2"
Java(TM) SE Runtime Environment (build 1.6.0-beta2-b83)
Java HotSpot(TM) Client VM (build 1.6.0-beta2-b83, mixed mode, sharing)
Robert
This is unlikely to be a bug. If you can tell us what these objects then we should be able to provide more information - and, as I mentioned in a previous reply, you can get more information by getting a JNI ref to them and either printing them out (meaning invoking the toString method and printing the String) and by querying the objects.
I would like to give you that information but I have no clue how to enable my JVMTI agent to call JNI function. After several trys I never got that JNIEnv pointer which is necessary to call that GetObjectClass() method you proposed for that.
I have tried JNI_CreateJavaVM, (*jvm)->AttachCurrentThread and (*jvm)->GetEnv, but nothing worked. Help needed.
Thanks Robert
Here's a code fragment that tags all "OTHER" roots and then uses GetObjectsWithTags to get a JNI refererence to them. With the JNI ref it prints out if it's an object or class. [This is just test code so no error checking, etc ]
<pre>
#include "jvmti.h"
#define MAGIC_VALUE99L
static JavaVM* jvm;
static jint JNICALL
cbHeapRef(jvmtiHeapReferenceKind reference_kind,
const jvmtiHeapReferenceInfo* reference_info,
jlong class_tag,
jlong referrer_class_tag,
jlong size,
jlong* tag_ptr,
jlong* referrer_tag_ptr,
jint length,
void* user_data)
{
if (reference_kind == JVMTI_HEAP_REFERENCE_OTHER) {
*tag_ptr = MAGIC_VALUE;
}
return 0;
}
static void JNICALL
dataDumpRequest(jvmtiEnv* jvmti)
{
JNIEnv* env;
jvmtiHeapCallbacks heap_callbacks;
jlong tag[1] = { MAGIC_VALUE };
jint i, count;
jobject* object;
(void)memset(&heap_callbacks,0,sizeof(heap_callbacks));
heap_callbacks.heap_reference_callback = &cbHeapRef;
(*jvmti)->FollowReferences(jvmti, 0, NULL, NULL, &heap_callbacks, NULL);
(*jvmti)->GetObjectsWithTags(jvmti, 1, &tag[0], &count, &object, NULL);
(*jvm)->GetEnv(jvm, (void**)&env, JNI_VERSION_1_4);
for (i=0; i < count; i++) {
jclass klass;
char* sig;
klass = (*env)->GetObjectClass(env, object);
(*jvmti)->GetClassSignature(jvmti, klass, &sig, NULL);
if (strcmp(sig, "Ljava/lang/Class;") == 0) {
char* class_sig;
jvmtiError err = (*jvmti)->GetClassSignature(jvmti, object, &class_sig, NULL);
if (err == JVMTI_ERROR_NONE) {
printf("class: %s\n", class_sig);
(*jvmti)->Deallocate(jvmti, class_sig);
} else {
printf("<not a class>\n");
}
} else {
printf("instance of %s\n", sig);
}
(*jvmti)->Deallocate(jvmti, sig);
}
}
</pre>
Ok, thanks ... In the mean time I found out by myself what the tricks is :-). One could cast the jvmtiEnv to a JNIEnv and than all functions are available. The results I could get with those I will find out tomorrow and post then. Thanks Robert
You can't cast a jvmtiEnv to a JNIEnv. If you need a JNIEnv in the data dump request callback then call GetEnv to obtain it - to do this you need to have stashed away a JavaVM reference which is easily done in the Agent_OnLoad.
As always ... you are right. But strange things happening ...
Casting the jvmtiEnv to a JNIEnv causes no errors AND is works with the following call:mid = (*theJNI)->GetMethodID(theJNI, klass, "toString", "()Ljava/lang/String;");
... when the theJNI is casted from a jvmtiEnv. But anything further (CallObjectMethod, ...) does not work. As I did it like you said it worked fine.
So here we go with the results:Ljava.lang.Class(*JVMTI unknown*) --1--> Ljava.lang.Class(Ljava.lang.Object) is a "class [[i" class
Ljava.lang.Class(Ljava.lang.Integer) --8--> Ljava.lang.Class(*JVMTI unknown*) is a "int" class
Ljava.lang.Class(Ljava.lang.Double) --8--> Ljava.lang.Class(*JVMTI unknown*) is a "double" class
Ljava.lang.Class(Ljava.lang.Boolean) --8--> Ljava.lang.Class(*JVMTI unknown*) is a "boolean" class
Ljava.lang.Class(Ljava.lang.Character) --8--> Ljava.lang.Class(*JVMTI unknown*) is a "char" class
Ljava.lang.Class(Ljava.lang.Byte) --8--> Ljava.lang.Class(*JVMTI unknown*) is a "byte" class
Ljava.lang.Class(Ljava.lang.Short) --8--> Ljava.lang.Class(*JVMTI unknown*) is a "short" class
Ljava.lang.Class(Ljava.lang.Long) --8--> Ljava.lang.Class(*JVMTI unknown*) is a "long" class
Ljava.lang.Class(Ljava.lang.Float) --8--> Ljava.lang.Class(*JVMTI unknown*) is a "float" class
Ljava.io.ObjectStreamField --2--> Ljava.lang.Class(*JVMTI unknown*) [appeared 6 times]
are "int double boolean boolean boolean long" classes (with the same IDs as those above)
The one missing is a "void" class. Now I think this issue is cleared, but one single point I do not understand is why that "class [[i" did not show up while the pre tagging of classes before all the iterate functions.
Thanks Robert
And a question for future releases ... will those OTHER classes in future releases still not appear at that pre tagging point or is that something which might change. That is important for the code I create and future compatibility reasons.Robert
By "pre tagging point" I assume you mean you enable the ClassLoad event and also get a list of already loaded classes with GetLoadedClasses. Assuming you do then you should be able to tag on classes prior to iteration. There will always be a small window in which a class is loaded and your agent's callback for ClassLoad hasn't executed by the time that the Iterate* or FollowReferences functions executes. There isn't a solution to that so it's best to always check if the class tag is 0.
As regards the primitive types then it should be clear from the spec for GetLoadedClasses:
"Array classes of all types (including arrays of primitive types) are included in the returned list. Primitive classes (for example, java.lang.Integer.TYPE) are not included in this list."
Finally, the roots reported as OTHER are just roots that don't fall into the other catagories. In the case of the HotSpot VM it just a small set of classes and instances (like important exceptions).
> By "pre tagging point" I assume you mean you enable the ClassLoad event and also get a list of already loaded classes with GetLoadedClasses.
I do only use the following events:
vmInit
vmDeath
dataDumpRequest
> As regards the primitive types then it should be clear from the spec for GetLoadedClasses:
> "Array classes of all types (including arrays of primitive types) are included in the returned list. Primitive classes (for example, java.lang.Integer.TYPE) are not included in this list."
I knew that and I was only wondering about that "[[I class" did not apear at class pre - tagging (by GetLoadedClasses prior to iterations). But if this is such delayed class loading issue (between pre tagging and iteration) I have to accept that. But is it possible to freeze the heap during the whole dataDumpRequest, not only during iterations? Or at least to disable GCs or/and class/object loading?
Thanks Robert
There isn't any way to freeze the heap or disable class loading. For non-array and non-primitive classes an agent will typically enable the ClassLoad event and tag the class in the callback.