bug in the ClassLoader
Tony Knaus
awk@spinnakernet.com
Sun Aug 26 13:25:00 GMT 2001
Hi,
While trying to build Apache Tomcat I have stumbled
across a condition where the buckets in the hash table implementing
the class cache in java/lang/natClassLoader.cc become populated with
cycles in the linked list. This causes the jvm to intermittently
hang when the class loader tries to find a class in the cache which
hashes to a bucket that has a cycle in it.
This bug occured when I built a separate shared
library with java security objects from the gnu classpath
to supply missing definitions to Tomcat I inadvertently
had a some duplication of the class name space between the
two libraries. Each one of the duplicated objects causes
a linked list pointer cycle in the class cache hash table.
Removing the duplication fixes the class cache.
Also the cycles only seems to occur when there is a
name collision between the name of an object in a shared
library and gcj libjava. My attempts to duplicate it using
arbitrarily choosen class names and libraries have failed.
Attached are instructions for duplicating this bug and
a gdb session displaying it.
-tony
To repeat class loader bug
1) Add the folloing debug stub to libjava/java/lang/natClassLoader.cc
void PrintClassPathStats()
{
_Jv_MonitorEnter (&ClassClass);
jclass klass;
int nfound=0;
for(int i=0; i<HASH_LEN; i++) {
for (klass = loaded_classes[i]; klass; klass = klass->next) {
nfound++;
fprintf(stderr," hash bucket: %d class %d name ", i, nfound);
for(int j=0; j<klass->name->length; j++) {
fprintf(stderr, "%c", (char) (klass->name->data[j]));
}
fprintf(stderr," klass=%p next=%p\n", klass, klass->next);
fflush(stderr);
if (klass == klass->next) break;
}
}
fprintf(stderr,"number of classes found: %d\n", nfound);
_Jv_MonitorExit (&ClassClass);
}
2) Make PrintClassPathStats a friend of jclass by adding
friend void PrintClassPathStats();
to libjava/java/lang/Class.h
3) recompile libjava
4) Using any one of the objects defined in libjava
and compile and link into a separate library shared
library. In this example I used java/security/DigestException.java
from the gnu classpath.
% /usr/local/gcc-3.0/bin/gcj -g --encoding=UTF-8 -shared -o libtest.so java/security/DigestException.o
5) Compile the following program and run gdb.
import java.util.ResourceBundle;
import java.security.DigestException;
public class TestClassLoader {
public static void main(String[] args) {
try {
} catch(DigestException e) {};
ResourceBundle b = ResourceBundle.getBundle("foo");
}
}
% /usr/local/gcc-3.0/bin/gcj -g --encoding=UTF-8 -CLASSPATH "." TestClassLoader.java -o TestClassLoader --main=TestClassLoader ./libtest.so -Wl,--rpath -Wl,/usr/local/gcc-3.0/lib -Wl,--rpath -Wl,/usr/local/gcc-3.0/lib/gcc-lib/i686-pc-linux-gnu/3.0.1
------------------------ gdb trace --------------------------------
> /usr/bin/gdb TestClassLoader
GNU gdb 19991004
Copyright 1998 Free Software Foundation, Inc.
GDB is free software, covered by the GNU General Public License, and you are
welcome to change it and/or distribute copies of it under certain conditions.
Type "show copying" to see the conditions.
There is absolutely no warranty for GDB. Type "show warranty" for details.
This GDB was configured as "i386-redhat-linux"...
(gdb) l 'TestClassLoader'
1 import java.util.ResourceBundle;
2 import java.security.DigestException;
3
4 public class TestClassLoader {
5 public static void main(String[] args) {
6 try {
7 } catch(DigestException e) {};
8
9 ResourceBundle b = ResourceBundle.getBundle("foo");
10 }
(gdb) b 9
Breakpoint 1 at 0x8048ea6: file TestClassLoader.java, line 9.
(gdb) run
Starting program: /home/awk/test_case/TestClassLoader
[New Thread 3403 (manager thread)]
[New Thread 3402 (initial thread)]
[New Thread 3404]
[Switching to Thread 3404]
Breakpoint 1, _ZN15TestClassLoader4mainEP6JArrayIPN4java4lang6StringEE (args=null) at TestClassLoader.java:9
9 ResourceBundle b = ResourceBundle.getBundle("foo");
Current language: auto; currently java
(gdb) l _Z19PrintClassPathStatsv
<skip to FindClassInCache>
(gdb) l
355
356 ^L
357
358 jclass
359 _Jv_FindClassInCache (_Jv_Utf8Const *name, java::lang::ClassLoader *loader)
360 {
361 _Jv_MonitorEnter (&ClassClass);
362 jint hash = HASH_UTF (name);
363
364 // first, if LOADER is a defining loader, then it is also initiating
(gdb) l
365 jclass klass;
366 for (klass = loaded_classes[hash]; klass; klass = klass->next)
367 {
368 if (loader == klass->loader && _Jv_equalUtf8Consts (name, klass->name))
369 break;
370 if (klass == klass->next) {
371
372 fprintf(stderr,"error loop in class loader klass=%p next=%p\n", klass, klass->next);
373 klass->next=0;
374 }
(gdb) b 368
Breakpoint 2 at 0x40177068: file ../../../gcc/libjava/java/lang/natClassLoader.cc, line 368.
(gdb) c
Continuing.
Breakpoint 2, _Z20_Jv_FindClassInCacheP13_Jv_Utf8ConstPN4java4lang11ClassLoaderE (name=0x8084f90, loader=0x0)
at ../../../gcc/libjava/java/lang/natClassLoader.cc:368
368 if (loader == klass->loader && _Jv_equalUtf8Consts (name, klass->name))
Current language: auto; currently c++
(gdb) call _Z19PrintClassPathStatsv()
hash bucket: 0 class 1 name java.security.DigestException klass=0x40015280 next=0x4033b6e0
hash bucket: 0 class 2 name java.net.ServerSocket klass=0x4033b6e0 next=0x40015280
hash bucket: 0 class 3 name java.security.DigestException klass=0x40015280 next=0x4033b6e0
hash bucket: 0 class 4 name java.net.ServerSocket klass=0x4033b6e0 next=0x40015280
hash bucket: 0 class 5 name java.security.DigestException klass=0x40015280 next=0x4033b6e0
hash bucket: 0 class 6 name java.net.ServerSocket klass=0x4033b6e0 next=0x40015280
hash bucket: 0 class 7 name java.security.DigestException klass=0x40015280 next=0x4033b6e0
hash bucket: 0 class 8 name java.net.ServerSocket klass=0x4033b6e0 next=0x40015280
hash bucket: 0 class 9 name java.security.DigestException klass=0x40015280 next=0x4033b6e0
hash bucket: 0 class 10 name java.net.ServerSocket klass=0x4033b6e0 next=0x40015280
[Switching to Thread 3402 (initial thread)]
Program received signal SIGINT, Interrupt.
0x4051a58b in __sigsuspend (set=0xbffff9e4) at ../sysdeps/unix/sysv/linux/sigsuspend.c:48
48 ../sysdeps/unix/sysv/linux/sigsuspend.c: No such file or directory.
The program being debugged stopped while in a function called from GDB.
When the function (_Z19PrintClassPathStatsv) is done executing, GDB will silently
stop (instead of continuing to evaluate the expression containing
the function call).
(gdb) quit
The program is running. Exit anyway? (y or n) y
Script done on Sun Aug 26 14:56:58 2001
More information about the Java
mailing list