Uploaded image for project: 'JDK'
  1. JDK
  2. JDK-4478849

JVMPI_OBJ_ALLOC posted instance size not correct

XMLWordPrintable

    • Icon: Bug Bug
    • Resolution: Fixed
    • Icon: P2 P2
    • 1.3.1_02
    • 1.3.0_01
    • vm-legacy
    • 02
    • sparc
    • solaris_8
    • Not verified



      Name: kb87695 Date: 07/11/2001


      Please evaluate the bug below to determine if it works as designed or if this is
      actually a bug.

      This bug could be reproduced with Solaris Java 1.3.1 Build 1.3.1-b24. The bug
      cannot be reporduced in Merlin latest.

      Basically, the problem is that the JVMPI_OBJ_ALLOC of java.lang.String have a
      posted object size which does not match the actual instance size. The same
      problem could happen to other classes, not necessarily only String.

      The testcase bwlow will print out all CLASS_LOAD/OBJ_ALLOC/OBJ_FREE/OBJ_MOVE
      events concering java.lang.String class. Note for this small example, No GC kick in
      at all, so OBJ_FREE/OBJ_MOVE just never happens.

      The problem is related to the TLE usage. If you turn off TLE by -XX:-UseTLE, it
      will print out same size for all instances.

      The problem seems only occuring with the server VM, but not with Cleint VM.

      hppdl694$ java -server -version
      java version "1.3.1"
      Java(TM) 2 Runtime Environment, Standard Edition (build 1.3.1-b24)
      Java HotSpot(TM) Server VM (build 1.3.1-b24, mixed mode)

      1, Save the objects.java file

      public class objects {
              public static void main(String[] args) {
                      int N = 1000;
                      for (int i = 0; i < N; i++) {
                              String str = new String();
                      }
              }
      }

      2, Save the jvmpi_tool.cpp file

      #include <stdio.h>
      #include <stdlib.h>
      #include <unistd.h>
      #include <string.h>
      #include <jvmpi.h>
      #ifdef _WINDOWS
      # include <windows.h>
      # define SLEEP(s) Sleep(s * 1000);
      #else
      # define SLEEP(s) sleep(s);
      #endif

      #define JVMPI(m) (jvmpi->m)

      static JVMPI_Interface *jvmpi;
      static void notify(JVMPI_Event *);


      extern "C" JNIEXPORT jint JNICALL
      JVM_OnLoad(JavaVM *jvm, char *options, void *reserved)
      {
              int res = jvm->GetEnv((void **)&jvmpi, JVMPI_VERSION_1);

              if (res < 0) {
                      return JNI_ERR;
              }

              jvmpi->NotifyEvent = notify;

              JVMPI(EnableEvent)(JVMPI_EVENT_JVM_INIT_DONE, NULL);
              JVMPI(EnableEvent)(JVMPI_EVENT_JVM_SHUT_DOWN, NULL);
              JVMPI(EnableEvent)(JVMPI_EVENT_CLASS_LOAD, NULL);
              JVMPI(EnableEvent)(JVMPI_EVENT_OBJECT_ALLOC, NULL);
              JVMPI(EnableEvent)(JVMPI_EVENT_OBJECT_MOVE, NULL);
              JVMPI(EnableEvent)(JVMPI_EVENT_OBJECT_FREE, NULL);
              JVMPI(EnableEvent)(JVMPI_EVENT_GC_START, NULL);
              JVMPI(EnableEvent)(JVMPI_EVENT_GC_FINISH, NULL);

              return JNI_OK;
      }

      static unsigned long moved, freed;
      static jobjectID stringClassID=(jobjectID)-1;

      void
      notify(JVMPI_Event * event)
      {
              jint result;

              switch (event->event_type) {
              case JVMPI_EVENT_JVM_INIT_DONE:
                      break;
              case JVMPI_EVENT_JVM_SHUT_DOWN:
                      printf("\nSHUT_DOWN");
                      break;
              case JVMPI_EVENT_CLASS_LOAD:
                      if (!strcmp(event->u.class_load.class_name, "java.lang.String") ||
                              !strcmp(event->u.class_load.class_name, "java/lang/String")) {
                              stringClassID=event->u.class_load.class_id;
                              printf("\nCLASS_LOAD: class_name: %s source_name: %s class_id %ld",
                                      event->u.class_load.class_name,
                                      event->u.class_load.source_name,
                                      event->u.class_load.class_id);
                      }
                      break;
              case JVMPI_EVENT_OBJECT_ALLOC:
                      if (event->u.obj_alloc.class_id==stringClassID) {
                         //printf("\nOBJECT_ALLOC: arena: %d class_id: %ld is_array: %d size %d obj_id %ld",
                              printf("\nOBJECT_ALLOC: arena: %d class_id: %p is_array: %d size %d obj_id %p",
                                      event->u.obj_alloc.arena_id,
                                      event->u.obj_alloc.class_id,
                                      event->u.obj_alloc.is_array,
                                      event->u.obj_alloc.size,
                                      event->u.obj_alloc.obj_id);
                      }
                      break;
              case JVMPI_EVENT_GC_START:
                      printf("\nGC_START");
                      moved = freed = 0;
                      break;
              case JVMPI_EVENT_OBJECT_MOVE:
                      if (event->u.obj_alloc.class_id==stringClassID) {
                              printf("\nOBJECT_MOVE: arena: %d obj_id: %ld new_arena_id: %d new_obj_id %ld",
                                      event->u.obj_move.arena_id,
                                      event->u.obj_move.obj_id,
                                      event->u.obj_move.new_arena_id,
                                      event->u.obj_move.new_obj_id);
                      }
                      ++moved;
                      break;
              case JVMPI_EVENT_OBJECT_FREE:
                      if (event->u.obj_alloc.class_id==stringClassID) {
                              printf("\nOBJECT_FREE: obj_id: %ld",
                                      event->u.obj_free.obj_id);
                      }
                      ++freed;
                      break;
              case JVMPI_EVENT_GC_FINISH:
                      printf("\nGC_FINISH: moved %lu, freed %lu\n", moved, freed);
                      fflush(stdout);
                      break;
              case JVMPI_REQUESTED_EVENT|JVMPI_EVENT_HEAP_DUMP:
                      printf("\nGot HEAP_DUMP");
                      break;
              default:
                      break;
              }
      }

      3, Save the makefile

      CXX = CC
      COMMON = -c
      CXXFLAGS = -I. -I/opt/jdk1.3.1/include -I/opt/jdk1.3.1/include/solaris $(COMMON)

      LDFLAGS = -G
      LD = ld

      all: libtest.so objects.class

      jvmpi_tool.o : jvmpi_tool.cpp
              $(CXX) $(CXXFLAGS) jvmpi_tool.cpp

      libtest.so: jvmpi_tool.o
              $(LD) $(LDFLAGS) -o libtest.so jvmpi_tool.o -lrt -lpthread

      objects.class : objects.java
              javac objects.java

      clean :
              rm *.class *.so *.o

      I assume the jdk1.3.1 is installed under /opt/jdk1.3.1 directory, and CC and LD installed
      properly in your PATH.

      4, Build everything by typing make. You should get the libtest.so and the objects.class
      files.

      5, Run the testcase now by typing
              java -server -cp . -Xruntest objects

      It is not happening with the default client VM, only with the server VM.

      You will see the output from the libtest.so giving the OBJ_ALLOC events for all instances
      of java.lang.String. The fields of each line is
              Arena ID, class ID, is_array indicator, bytesize of objects, object ID

      6, What is the problem?

      Part of the out:

      OBJECT_ALLOC: arena: 4 class_id: f6809f98 is_array: 0 size 24 obj_id f3d72960
      OBJECT_ALLOC: arena: 4 class_id: f6809f98 is_array: 0 size 24 obj_id f3d729a8
      OBJECT_ALLOC: arena: 4 class_id: f6809f98 is_array: 0 size 24 obj_id f3d729f0
      OBJECT_ALLOC: arena: 4 class_id: f6809f98 is_array: 2 size 16 obj_id f3d72a08
      OBJECT_ALLOC: arena: 4 class_id: f6809f98 is_array: 0 size 24 obj_id f3d72a18
      OBJECT_ALLOC: arena: 4 class_id: f6809f98 is_array: 0 size 16384 obj_id f3d74358 <--- 16384 but not 24
      OBJECT_ALLOC: arena: 4 class_id: f6809f98 is_array: 0 size 24 obj_id f3d74358
      OBJECT_ALLOC: arena: 4 class_id: f6809f98 is_array: 0 size 16384 obj_id f3d78358 <--- 16384 but not 24
      OBJECT_ALLOC: arena: 4 class_id: f6809f98 is_array: 0 size 24 obj_id f3d78358
      OBJECT_ALLOC: arena: 4 class_id: f6809f98 is_array: 0 size 16384 obj_id f3d7c358 <--- 16384 but not 24
      OBJECT_ALLOC: arena: 4 class_id: f6809f98 is_array: 0 size 24 obj_id f3d7c358
      OBJECT_ALLOC: arena: 5 class_id: f6809f98 is_array: 0 size 24 obj_id f68a6798

      The spurious ones are pointed by the arrow. While the instance size is actually
      24 bytes on solaris, three String object allocation is posted with 16384 bytes.

      7, How did this happen?

      In the debugger I saw that the to-be-allocated object could not fit into the
      TLE, hence a TLE expansion is supposed to happen. In universe::clear_allocate_local,
      the new_tle_size is computed, this new_tle_size is of 16384 bytes. Then the VM does
      Universe::clear_allocate_shared(klass, new_tle_size, CHECK_0) to allocate the
      String object. In clear_allocate_shared it will post the JVMPI_OBJ_ALLOC_EVENT
      with the new_tle_size. This is how this non-matched String object size comes into
      the picture. If turning off TLE by flag -XX:-UseTLE, it will go away.

      I don't know if this is a design issue or an impl issue. If it is a design issue
      and it is working as designed, then it's ok, I will withdraw the bug report and
      tell our customer. Otherwise we'd like to have a fix ASAP.
      (Review ID: 127896)
      ======================================================================

            sundar Sundararajan Athijegannathan
            kevibrow Kevin Brown
            Votes:
            0 Vote for this issue
            Watchers:
            0 Start watching this issue

              Created:
              Updated:
              Resolved:
              Imported:
              Indexed: