On Wed, 11 Oct 2000, Daniel Wickstrom wrote:
 
> Last weekend I ran the merged tclblend/aolserver combination using
> apache-bench to make concurrent accesses of multiple urls, and I
> noticed that the memory size was growing over time.  I think this is
> probably due in part to the java info cache not being cleaned up.  It
> seems that at the end of a thread, the DeleteGlobalRef should be
> called for each of the items in the cache.

That does seem logical. It looks like the Class refs need to
be cleaned up. What do you think of the patch below? This
would tend to happen if you created a lot of Tcl or Java
threads and loaded Tcl Blend into them. Is that what
you are doing? This also sounds like that mem leak problem
from yesterday, do this patch help? The patch is for the
ajuba-tclblend-contract-2000-08-01-branch branch in the CVS.

> Another thing that I've noticed is that the TclThreadCleanup routine
> is not being called.  I register this proc one time from the aolserver
> startup routine, but I'm wondering if maybe it needs to be registered
> at the start of each thread.
> 
> -Dan

That seems to happen to me too. It seems like the new cache
cleanup method is getting called when Tcl Blend is loaded into
Tcl, but I am not sure what is going on when you load
Tcl Blend and Tcl into a JVM. How could we test this
sort of thing?

Here is my proposed patch to cleanup the Class refs.
I am also going to attach it to this file because
it is going to get hosed in mail.

Mo DeJong
Red Hat Inc


Index: tcljava/src/native/javaCmd.c
===================================================================
RCS file: /home/cvs/external/tcljava/src/native/javaCmd.c,v
retrieving revision 1.9.2.7
diff -u -r1.9.2.7 javaCmd.c
--- javaCmd.c   2000/08/27 08:07:40     1.9.2.7
+++ javaCmd.c   2000/10/12 06:00:15
@@ -104,6 +104,7 @@
 static int             AddToFieldCache(JNIEnv *env, Tcl_Interp *interp, jfieldID 
*addr,
                             char *name, jclass *class, char *sig);
 static void            TclThreadCleanup(ClientData clientData);
+static void            JavaCacheCleanup(ClientData clientData);
 
 /*
  *----------------------------------------------------------------------
@@ -206,7 +207,7 @@
 
 #ifdef TCLBLEND_DEBUG
     fprintf(stderr, "TCLBLEND_DEBUG: Tclblend_Init finished\n");
-    fprintf(stderr, "TCLBLEND_DEBUG: JavaInitBlend() returned ");
+    fprintf(stderr, "TCLBLEND_DEBUG: JavaInitBlend returned ");
     if (result == TCL_ERROR) {
       fprintf(stderr, "TCL_ERROR");
     } else if (result == TCL_OK) {
@@ -393,6 +394,8 @@
      * From this point on, deal with the case where Tcl Blend is loaded 
from Tcl.
      * Check to see if the current process already has a Java VM.  If 
so, attach
      * the current thread to it, otherwise create a new JVM (automatic 
thread attach).
+     * In the case where Tcl Blend is loaded into Java, the call to
+     * JNI_GetCreatedJavaVMs will fill in the javaVM pointer with the 
JVM pointer.
      */
 
     if (JNI_GetCreatedJavaVMs(&javaVM, 1, &nVMs) < 0) {
@@ -558,7 +561,7 @@
             goto error;
        }
 
-    } else {
+    } else { /* (nVMs == 0) */
 
 #ifdef TCLBLEND_DEBUG
     fprintf(stderr, "TCLBLEND_DEBUG: JVM in process, attaching\n");
@@ -734,20 +737,66 @@
 static void
 TclThreadCleanup(ClientData clientData)
 {
-    JNIEnv* env;
-    ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
-
 #ifdef TCLBLEND_DEBUG
     fprintf(stderr, "TCLBLEND_DEBUG: called TclThreadCleanup\n");
 #endif /* TCLBLEND_DEBUG */
 
-    env = tsdPtr->currentEnv;
     (*javaVM)->DetachCurrentThread(javaVM);
 }
 
 /*
  *----------------------------------------------------------------------
  *
+ * JavaCacheCleanup --
+ *
+ *     This method will be called when a Tcl or Java thread is finished.
+ *     It needs to remove any global cache references so that the
+ *     classes and methods can be cleaned up by the JVM.
+ *
+ * Results:
+ *     None.
+ *
+ * Side effects:
+ *     None.
+ *
+ *----------------------------------------------------------------------
+ */
+static void
+JavaCacheCleanup(ClientData clientData)
+{
+    JNIEnv* env = JavaGetEnv();
+    JavaInfo* jcache = JavaGetCache();
+
+/* FIXME: need to add code to check for case where TclThreadCleanup is 
called first */
+
+#ifdef TCLBLEND_DEBUG
+    fprintf(stderr, "TCLBLEND_DEBUG: called JavaCacheCleanup\n");
+#endif /* TCLBLEND_DEBUG */
+
+    /* We need to delete any global refs to Java classes */
+    
+    (*env)->DeleteGlobalRef(env, jcache->Object);
+    (*env)->DeleteGlobalRef(env, jcache->Interp);
+    (*env)->DeleteGlobalRef(env, jcache->Command);
+    (*env)->DeleteGlobalRef(env, jcache->TclObject);
+    (*env)->DeleteGlobalRef(env, jcache->TclException);
+    (*env)->DeleteGlobalRef(env, jcache->CommandWithDispose);
+    (*env)->DeleteGlobalRef(env, jcache->CObject);
+    (*env)->DeleteGlobalRef(env, jcache->Extension);
+    (*env)->DeleteGlobalRef(env, jcache->VarTrace);
+    (*env)->DeleteGlobalRef(env, jcache->Void);
+    (*env)->DeleteGlobalRef(env, jcache->BlendExtension);
+    (*env)->DeleteGlobalRef(env, jcache->Notifier);
+    (*env)->DeleteGlobalRef(env, jcache->IdleHandler);
+    (*env)->DeleteGlobalRef(env, jcache->TimerHandler);
+
+     /* FIXME : we dont add or release a global ref for jcache->Void
+        or jcache->VoidTYPE class, should we ? */
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
  * JavaSetupJava --
  *
  *     This is the entry point for a Tcl interpreter created from Java.
@@ -830,6 +879,19 @@
     }
 
     /*
+     * Get the Void.TYPE class.
+     */
+
+    field = (*env)->GetStaticFieldID(env, jcache->Void, "TYPE", 
"Ljava/lang/Class;");
+    jcache->voidTYPE = (*env)->GetStaticObjectField(env, jcache->Void, 
field);
+
+    /*
+     * Create a thread exit handler that will clean up the cache.
+     */
+
+    Tcl_CreateThreadExitHandler(JavaCacheCleanup, NULL);
+
+    /*
      * Load methods needed by this module.
      */
 
@@ -881,13 +943,6 @@
         AddToFieldCache(env, interp, &jcache->objPtr, "objPtr", 
&jcache->CObject, "J")) {
        goto error;
     }
-
-    /*
-     * Get the Void.TYPE value.
-     */
-
-    field = (*env)->GetStaticFieldID(env, jcache->Void, "TYPE", 
"Ljava/lang/Class;");
-    jcache->voidTYPE = (*env)->GetStaticObjectField(env, jcache->Void, 
field);
 
     /*
      * Register the Java object types.
Index: tcljava/src/native/javaCmd.c
===================================================================
RCS file: /home/cvs/external/tcljava/src/native/javaCmd.c,v
retrieving revision 1.9.2.7
diff -u -r1.9.2.7 javaCmd.c
--- javaCmd.c   2000/08/27 08:07:40     1.9.2.7
+++ javaCmd.c   2000/10/12 06:00:15
@@ -104,6 +104,7 @@
 static int             AddToFieldCache(JNIEnv *env, Tcl_Interp *interp, jfieldID 
*addr,
                             char *name, jclass *class, char *sig);
 static void            TclThreadCleanup(ClientData clientData);
+static void            JavaCacheCleanup(ClientData clientData);
 
 /*
  *----------------------------------------------------------------------
@@ -206,7 +207,7 @@
 
 #ifdef TCLBLEND_DEBUG
     fprintf(stderr, "TCLBLEND_DEBUG: Tclblend_Init finished\n");
-    fprintf(stderr, "TCLBLEND_DEBUG: JavaInitBlend() returned ");
+    fprintf(stderr, "TCLBLEND_DEBUG: JavaInitBlend returned ");
     if (result == TCL_ERROR) {
       fprintf(stderr, "TCL_ERROR");
     } else if (result == TCL_OK) {
@@ -393,6 +394,8 @@
      * From this point on, deal with the case where Tcl Blend is loaded from Tcl.
      * Check to see if the current process already has a Java VM.  If so, attach
      * the current thread to it, otherwise create a new JVM (automatic thread 
attach).
+     * In the case where Tcl Blend is loaded into Java, the call to
+     * JNI_GetCreatedJavaVMs will fill in the javaVM pointer with the JVM pointer.
      */
 
     if (JNI_GetCreatedJavaVMs(&javaVM, 1, &nVMs) < 0) {
@@ -558,7 +561,7 @@
             goto error;
        }
 
-    } else {
+    } else { /* (nVMs == 0) */
 
 #ifdef TCLBLEND_DEBUG
     fprintf(stderr, "TCLBLEND_DEBUG: JVM in process, attaching\n");
@@ -734,20 +737,66 @@
 static void
 TclThreadCleanup(ClientData clientData)
 {
-    JNIEnv* env;
-    ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
-
 #ifdef TCLBLEND_DEBUG
     fprintf(stderr, "TCLBLEND_DEBUG: called TclThreadCleanup\n");
 #endif /* TCLBLEND_DEBUG */
 
-    env = tsdPtr->currentEnv;
     (*javaVM)->DetachCurrentThread(javaVM);
 }
 
 /*
  *----------------------------------------------------------------------
  *
+ * JavaCacheCleanup --
+ *
+ *     This method will be called when a Tcl or Java thread is finished.
+ *     It needs to remove any global cache references so that the
+ *     classes and methods can be cleaned up by the JVM.
+ *
+ * Results:
+ *     None.
+ *
+ * Side effects:
+ *     None.
+ *
+ *----------------------------------------------------------------------
+ */
+static void
+JavaCacheCleanup(ClientData clientData)
+{
+    JNIEnv* env = JavaGetEnv();
+    JavaInfo* jcache = JavaGetCache();
+
+/* FIXME: need to add code to check for case where TclThreadCleanup is called first 
+*/
+
+#ifdef TCLBLEND_DEBUG
+    fprintf(stderr, "TCLBLEND_DEBUG: called JavaCacheCleanup\n");
+#endif /* TCLBLEND_DEBUG */
+
+    /* We need to delete any global refs to Java classes */
+    
+    (*env)->DeleteGlobalRef(env, jcache->Object);
+    (*env)->DeleteGlobalRef(env, jcache->Interp);
+    (*env)->DeleteGlobalRef(env, jcache->Command);
+    (*env)->DeleteGlobalRef(env, jcache->TclObject);
+    (*env)->DeleteGlobalRef(env, jcache->TclException);
+    (*env)->DeleteGlobalRef(env, jcache->CommandWithDispose);
+    (*env)->DeleteGlobalRef(env, jcache->CObject);
+    (*env)->DeleteGlobalRef(env, jcache->Extension);
+    (*env)->DeleteGlobalRef(env, jcache->VarTrace);
+    (*env)->DeleteGlobalRef(env, jcache->Void);
+    (*env)->DeleteGlobalRef(env, jcache->BlendExtension);
+    (*env)->DeleteGlobalRef(env, jcache->Notifier);
+    (*env)->DeleteGlobalRef(env, jcache->IdleHandler);
+    (*env)->DeleteGlobalRef(env, jcache->TimerHandler);
+
+     /* FIXME : we dont add or release a global ref for jcache->Void
+        or jcache->VoidTYPE class, should we ? */
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
  * JavaSetupJava --
  *
  *     This is the entry point for a Tcl interpreter created from Java.
@@ -830,6 +879,19 @@
     }
 
     /*
+     * Get the Void.TYPE class.
+     */
+
+    field = (*env)->GetStaticFieldID(env, jcache->Void, "TYPE", "Ljava/lang/Class;");
+    jcache->voidTYPE = (*env)->GetStaticObjectField(env, jcache->Void, field);
+
+    /*
+     * Create a thread exit handler that will clean up the cache.
+     */
+
+    Tcl_CreateThreadExitHandler(JavaCacheCleanup, NULL);
+
+    /*
      * Load methods needed by this module.
      */
 
@@ -881,13 +943,6 @@
         AddToFieldCache(env, interp, &jcache->objPtr, "objPtr", &jcache->CObject, 
"J")) {
        goto error;
     }
-
-    /*
-     * Get the Void.TYPE value.
-     */
-
-    field = (*env)->GetStaticFieldID(env, jcache->Void, "TYPE", "Ljava/lang/Class;");
-    jcache->voidTYPE = (*env)->GetStaticObjectField(env, jcache->Void, field);
 
     /*
      * Register the Java object types.

Reply via email to