-
Bug
-
Resolution: Fixed
-
P2
-
5.0
-
b51
-
generic
-
solaris_8
The use of annotation-valued annotation elements causes class loaders that
contain the annotation types to be retained. This results in a memory leak
and may prevent the use of annotations in some enterprise applications.
==========$ ls -la
total 28
drwxr-xr-x 2 gafter 4096 Apr 30 10:03 .
drwxr-xr-x 17 gafter 4096 Apr 29 22:33 ..
-rw-r--r-- 1 gafter 113 Apr 30 09:41 A.java
-rw-r--r-- 1 gafter 23 Apr 30 09:31 B.java
-rw-r--r-- 1 gafter 29 Apr 30 09:31 C.java
-rw-r--r-- 1 gafter 3001 Apr 30 09:44 Main.java
-rw-r--r-- 1 gafter 300 Apr 30 09:58 Makefile
==========$ for file in *; do echo //////////////////// $file ; cat -n $file; done
//////////////////// A.java
1 public
2 @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.RUNTIME)
3 @interface A {
4 B b();
5 }
//////////////////// B.java
1 public @interface B {}
//////////////////// C.java
1 public @A(b=@B()) class C {}
//////////////////// Main.java
1 import java.net.*;
2 import java.lang.ref.*;
3 import java.util.*;
4 import java.io.*;
5
6 public class Main {
7 public static void main(String[] args) throws Exception {
8 for (int i=0; i<100; i++)
9 doTest(args.length != 0);
10 }
11
12 static void doTest(boolean readAnn) throws Exception {
13 // URL classes = new URL("file://" + System.getProperty("user.dir") + "/classes");
14 // URL[] path = { classes };
15 // URLClassLoader loader = new URLClassLoader(path);
16 ClassLoader loader = new SimpleClassLoader();
17 WeakReference<Class<?>> c = new WeakReference(loader.loadClass("C"));
18 if (c.get() == null) throw new AssertionError();
19 if (c.get().getClassLoader() != loader) throw new AssertionError();
20 if (readAnn) System.out.println(c.get().getAnnotations()[0]);
21 if (c.get() == null) throw new AssertionError();
22 System.gc();
23 System.gc();
24 if (c.get() == null) throw new AssertionError();
25 System.gc();
26 System.gc();
27 loader = null;
28 System.gc();
29 System.gc();
30 if (c.get() != null) throw new AssertionError();
31 }
32 }
33
34 class SimpleClassLoader extends ClassLoader {
35 private Hashtable classes = new Hashtable();
36
37 public SimpleClassLoader() {
38 }
39 private byte getClassImplFromDataBase(String className)[] {
40 byte result[];
41 try {
42 FileInputStream fi = new FileInputStream("classes/"+className+".class");
43 result = new byte[fi.available()];
44 fi.read(result);
45 return result;
46 } catch (Exception e) {
47
48 /*
49 * If we caught an exception, either the class wasnt found or it
50 * was unreadable by our process.
51 */
52 return null;
53 }
54 }
55 public Class loadClass(String className) throws ClassNotFoundException {
56 return (loadClass(className, true));
57 }
58 public synchronized Class loadClass(String className, boolean resolveIt)
59 throws ClassNotFoundException {
60 Class result;
61 byte classData[];
62
63 /* Check our local cache of classes */
64 result = (Class)classes.get(className);
65 if (result != null) {
66 return result;
67 }
68
69 /* Check with the primordial class loader */
70 try {
71 result = super.findSystemClass(className);
72 return result;
73 } catch (ClassNotFoundException e) {
74 }
75
76 /* Try to load it from our repository */
77 classData = getClassImplFromDataBase(className);
78 if (classData == null) {
79 throw new ClassNotFoundException();
80 }
81
82 /* Define it (parse the class file) */
83 result = defineClass(classData, 0, classData.length);
84 if (result == null) {
85 throw new ClassFormatError();
86 }
87
88 if (resolveIt) {
89 resolveClass(result);
90 }
91
92 classes.put(className, result);
93 return result;
94 }
95 }
//////////////////// Makefile
1 JAVAC=/java/re/j2se/1.5.0/promoted/beta2/b49/binaries/solaris-sparc/bin/javac
2 JAVA=/java/re/j2se/1.5.0/promoted/beta2/b49/binaries/solaris-sparc/bin/java
3
4 all:
5 mkdir -p classes
6 $(JAVAC) -d classes A.java B.java C.java
7 $(JAVAC) Main.java
8 # this works
9 $(JAVA) Main
10 # this fails
11 $(JAVA) Main foo
==========$ make
mkdir -p classes
/java/re/j2se/1.5.0/promoted/beta2/b49/binaries/solaris-sparc/bin/javac -d classes A.java B.java C.java
/java/re/j2se/1.5.0/promoted/beta2/b49/binaries/solaris-sparc/bin/javac Main.java
Note: Main.java uses or overrides a deprecated API.
/java/re/j2se/1.5.0/promoted/beta2/b49/binaries/solaris-sparc/bin/javac Main.java
Note: Main.java uses or overrides a deprecated API.
Note: Recompile with -Xlint:deprecation for details.
Note: Main.java uses unchecked or unsafe operations.
Note: Recompile with -Xlint:unchecked for details.
# this works
/java/re/j2se/1.5.0/promoted/beta2/b49/binaries/solaris-sparc/bin/java Main
# this fails
/java/re/j2se/1.5.0/promoted/beta2/b49/binaries/solaris-sparc/bin/java Main foo
@A(b=@B())
Exception in thread "main" java.lang.AssertionError
at Main.doTest(Main.java:30)
at Main.main(Main.java:9)
make: *** [all] Error 1
==========$
contain the annotation types to be retained. This results in a memory leak
and may prevent the use of annotations in some enterprise applications.
==========$ ls -la
total 28
drwxr-xr-x 2 gafter 4096 Apr 30 10:03 .
drwxr-xr-x 17 gafter 4096 Apr 29 22:33 ..
-rw-r--r-- 1 gafter 113 Apr 30 09:41 A.java
-rw-r--r-- 1 gafter 23 Apr 30 09:31 B.java
-rw-r--r-- 1 gafter 29 Apr 30 09:31 C.java
-rw-r--r-- 1 gafter 3001 Apr 30 09:44 Main.java
-rw-r--r-- 1 gafter 300 Apr 30 09:58 Makefile
==========$ for file in *; do echo //////////////////// $file ; cat -n $file; done
//////////////////// A.java
1 public
2 @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.RUNTIME)
3 @interface A {
4 B b();
5 }
//////////////////// B.java
1 public @interface B {}
//////////////////// C.java
1 public @A(b=@B()) class C {}
//////////////////// Main.java
1 import java.net.*;
2 import java.lang.ref.*;
3 import java.util.*;
4 import java.io.*;
5
6 public class Main {
7 public static void main(String[] args) throws Exception {
8 for (int i=0; i<100; i++)
9 doTest(args.length != 0);
10 }
11
12 static void doTest(boolean readAnn) throws Exception {
13 // URL classes = new URL("file://" + System.getProperty("user.dir") + "/classes");
14 // URL[] path = { classes };
15 // URLClassLoader loader = new URLClassLoader(path);
16 ClassLoader loader = new SimpleClassLoader();
17 WeakReference<Class<?>> c = new WeakReference(loader.loadClass("C"));
18 if (c.get() == null) throw new AssertionError();
19 if (c.get().getClassLoader() != loader) throw new AssertionError();
20 if (readAnn) System.out.println(c.get().getAnnotations()[0]);
21 if (c.get() == null) throw new AssertionError();
22 System.gc();
23 System.gc();
24 if (c.get() == null) throw new AssertionError();
25 System.gc();
26 System.gc();
27 loader = null;
28 System.gc();
29 System.gc();
30 if (c.get() != null) throw new AssertionError();
31 }
32 }
33
34 class SimpleClassLoader extends ClassLoader {
35 private Hashtable classes = new Hashtable();
36
37 public SimpleClassLoader() {
38 }
39 private byte getClassImplFromDataBase(String className)[] {
40 byte result[];
41 try {
42 FileInputStream fi = new FileInputStream("classes/"+className+".class");
43 result = new byte[fi.available()];
44 fi.read(result);
45 return result;
46 } catch (Exception e) {
47
48 /*
49 * If we caught an exception, either the class wasnt found or it
50 * was unreadable by our process.
51 */
52 return null;
53 }
54 }
55 public Class loadClass(String className) throws ClassNotFoundException {
56 return (loadClass(className, true));
57 }
58 public synchronized Class loadClass(String className, boolean resolveIt)
59 throws ClassNotFoundException {
60 Class result;
61 byte classData[];
62
63 /* Check our local cache of classes */
64 result = (Class)classes.get(className);
65 if (result != null) {
66 return result;
67 }
68
69 /* Check with the primordial class loader */
70 try {
71 result = super.findSystemClass(className);
72 return result;
73 } catch (ClassNotFoundException e) {
74 }
75
76 /* Try to load it from our repository */
77 classData = getClassImplFromDataBase(className);
78 if (classData == null) {
79 throw new ClassNotFoundException();
80 }
81
82 /* Define it (parse the class file) */
83 result = defineClass(classData, 0, classData.length);
84 if (result == null) {
85 throw new ClassFormatError();
86 }
87
88 if (resolveIt) {
89 resolveClass(result);
90 }
91
92 classes.put(className, result);
93 return result;
94 }
95 }
//////////////////// Makefile
1 JAVAC=/java/re/j2se/1.5.0/promoted/beta2/b49/binaries/solaris-sparc/bin/javac
2 JAVA=/java/re/j2se/1.5.0/promoted/beta2/b49/binaries/solaris-sparc/bin/java
3
4 all:
5 mkdir -p classes
6 $(JAVAC) -d classes A.java B.java C.java
7 $(JAVAC) Main.java
8 # this works
9 $(JAVA) Main
10 # this fails
11 $(JAVA) Main foo
==========$ make
mkdir -p classes
/java/re/j2se/1.5.0/promoted/beta2/b49/binaries/solaris-sparc/bin/javac -d classes A.java B.java C.java
/java/re/j2se/1.5.0/promoted/beta2/b49/binaries/solaris-sparc/bin/javac Main.java
Note: Main.java uses or overrides a deprecated API.
/java/re/j2se/1.5.0/promoted/beta2/b49/binaries/solaris-sparc/bin/javac Main.java
Note: Main.java uses or overrides a deprecated API.
Note: Recompile with -Xlint:deprecation for details.
Note: Main.java uses unchecked or unsafe operations.
Note: Recompile with -Xlint:unchecked for details.
# this works
/java/re/j2se/1.5.0/promoted/beta2/b49/binaries/solaris-sparc/bin/java Main
# this fails
/java/re/j2se/1.5.0/promoted/beta2/b49/binaries/solaris-sparc/bin/java Main foo
@A(b=@B())
Exception in thread "main" java.lang.AssertionError
at Main.doTest(Main.java:30)
at Main.main(Main.java:9)
make: *** [all] Error 1
==========$