river-dev mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From Gregg Wonderly <gr...@wonderly.org>
Subject Re: ClassLoader and Class Garbage Collection issues with Serialization.
Date Mon, 12 Apr 2010 15:07:09 GMT
It was my understanding that this issue had been solved as well.

Gregg Wonderly

Christopher Dolan wrote:
> This is news to me.  Looking at the JDK 1.5 source code, I can see that
> ObjectStreamClass$Caches uses weak references for the classes.  So
> either this is a solved problem as of 1.5, or there's something
> non-obvious in the implementation that I've overlooked.
> 
> Chris
> 
> -----Original Message-----
> From: Peter Firmstone [mailto:jini@zeus.net.au] 
> Sent: Saturday, April 10, 2010 8:25 PM
> To: river-dev@incubator.apache.org
> Subject: ClassLoader and Class Garbage Collection issues with
> Serialization.
> 
> I have stumbled across a troubling problem with Serialization relating 
> to Garbage Collection of Classes and ClassLoaders and was hoping someone
> 
> might be able to shed some light on the issue.
> 
> Is it really true that the more objects you distribute, the greater your
> 
> memory consumption because Class files and ClassLoaders cannot be 
> garbage collected?
> 
> Regards,
> 
> Peter.
> 
> The issue can be found here:
> http://www.ibm.com/developerworks/java/library/j-dclp3/index.html
> 
> And here's the relevant information, pasted from the link:
> 
> 
>   Problems related to garbage collection and serialization
> 
> The garbage collector interacts closely with the class loader. Among 
> other things, the collector examines the class loader data structures to
> 
> determine which classes are /live/ -- that is, are not garbage 
> collectable. This can often lead to some unexpected problems.
> 
> Figure 2 illustrates a situation where serialization affects the garbage
> 
> collection (GC) of classes and a class loader in an unexpected way:
> 
> 
>     *Figure 2. Serialization example*
> 
> Serialization example
> 
> In this example, |SerializationTest| instantiates a |URLClassLoader|, 
> called |loader|. After loading |SerializationClass|, the class loader is
> 
> dereferenced. The expectation is that this will allow the classes loaded
> 
> by it to be garbage collected. The code for these classes is illustrated
> 
> in Listings 9 and 10:
> 
> 
> *Listing 9. SerializationTest.java*
> 
> import java.net.MalformedURLException;
> import java.net.URL;
> import java.net.URLClassLoader;
> 
> public class SerializationTest extends ClassLoader {
> 
>    public static void main(String args[]) {
>       try {
>          URLClassLoader loader = new URLClassLoader(new URL[] { new URL(
>                "file://C:/CL_Article/Serialization/dir1/") });
>          System.out.println("Loading SerializationClass");
>          Class c = loader.loadClass("SerializationClass");
>          System.out.println("Creating an instance of
> SerializationClass");
>          c.newInstance();
>          System.out.println("Dereferencing the class loader");
>          c = null;
>          loader = null;
>          
>          System.out.println("Running GC...");
>          System.gc();
>          System.out.println("Triggering a Javadump");
>          com.ibm.jvm.Dump.JavaDump();
>          
>       } catch (MalformedURLException e) {
>          e.printStackTrace();
>       } catch (InstantiationException e) {
>          e.printStackTrace();
>       } catch (IllegalAccessException e) {
>          e.printStackTrace();
>       } catch (ClassNotFoundException e) {
>          e.printStackTrace();
>       }
>    }
> }
> 
> 
> 
> *Listing 10. SerializationClass.java*
> 
> import java.io.File;
> import java.io.FileOutputStream;
> import java.io.ObjectOutputStream;
> import java.io.Serializable;
> 
> public class SerializationClass implements Serializable {
> 
>     private static final long serialVersionUID = 5024741671582526226L;
> 
>     public SerializationClass() {
>         try {
>             File file = new
> File("C:/CL_Article/Serialization/test.txt");
>             FileOutputStream fos = new FileOutputStream(file);
>             ObjectOutputStream oos = new ObjectOutputStream(fos);
>             oos.writeObject(this);
>             oos.reset();
>             oos.close();
>             fos.close();
>             oos = null;
>             fos = null;
>             file = null;
>         } catch (Exception e) {
>             e.printStackTrace();
>         }
>     }
> }
> 
> 
> Using a Javadump, it is possible to discover whether the class loader 
> has been garbage collected. (See the first article in this series for 
> more on using Javadump.) If the following section appears in the list of
> 
> class loaders, then it has not been collected:
> 
> ------a- Loader java/net/URLClassLoader(0x44DC6DE0), Shadow 0x00ADB6D8,
>         Parent sun/misc/Launcher$AppClassLoader(0x00ADB7B0) 
>         Number of loaded classes 1 
>         Number of cached classes 11      
>         Allocation used for loaded classes 1      
>         Package owner 0x00ADB6D8
>       
> 
> 
> Though dereferencing a user-defined class loader seems like a way to 
> ensure that the classes are garbage collected, this is not actually the 
> case. In the previous example, the problem stems from the use of 
> |java.io.ObjectOutputStream.writeObject(Object obj)| and its 
> implications on GC.
> 
> When |writeObject()| is invoked (to serialize |SerializationClass|), a 
> reference to this class object is passed internally to 
> |ObjectStreamClass| and stored in a lookup table (that is, in an 
> internal cache). This reference is kept to speed up future serialization
> 
> of the same class.
> 
> When the class loader is dereferenced, the classes that it loaded are 
> not garbage collectable. This is because there is a live reference to 
> the |SerializationClass| class from the |ObjectStreamClass| lookup 
> table. |ObjectStreamClass| is a primordial class and therefore is never 
> garbage collected. The lookup table is referenced from a static field in
> 
> |ObjectStreamClass| and is kept in the class itself rather than in an 
> instance of it. As a result, the reference to |SerializationClass| 
> exists for the lifetime of the JVM, and the class thus cannot be garbage
> 
> collected. Importantly, the |SerializationClass| class has a reference 
> to its defining class loader, and so it cannot be completely 
> dereferenced either.
> 
> To avoid this problem, any classes that are to be serialized should be 
> loaded by a class loader that does not need to be garbage collected -- 
> by the system class loader, for example.
> 
> 
> 


Mime
View raw message