|
Description
|
The JDK has some limited support for "unloading"
classes through making the class, its instances, and
its ClassLoader become garbage collected.
I think this current level of support is insufficient
for building systems that allow incremental
("fix and continue") development of Java code.
This kind of support is important for two reasons.
One, in the development of many appliations, it
is necessary to debug and test a small part of
the code that operates after a lot of "state"
has been created in the runtime (big data structures,
interactive state, etc.); restarting the runtime
again and again for every code change is very slow.
"Development" may not be a Java programmer, but also
a system that generates code from a GUI on behalf
of the user. Another important use of unloading/reloading
classes is the seamless upgrading of long-running
servers. Right now, parts of a long-running
Java server can be upgraded without shutting down
the server only if they have been architected for upgrading
by using separate class loaders.
There are several levels of functionality that can
be provided that go beyond what Java offers
right now, with fairly straightforward implementations
and some history of actual use.
(1) I should be able to reload code implementing
the methods of a class if all the method signatures
and instance variables remain unchanged. This
would not affect the integrity of the type system.
(2) There should be some way of reloading a class
even if its instance variables and/or method
signatures change. The way this is commonly
handled is to "rename" the existing loaded
version of a class and then load the new version
of the class under the old name.
"Renaming class A to class B" in the runtime means changing
its internal state such that it looks as if
all the files that have been loaded and referred
to class A had been changed to refer to class B.
This sounds complicated, but in most runtimes
for languages like Java, it usually only
involves altering the name of the class in the
Class structure; the behavior for natively
compiled code &c. automatically works out.
With "rename", class reloading could look
something like:
static void unload_by_rename(Class cls) {
// rename the class to some non-existent
// class name
for(int v=1;;v++) {
try { cls.rename("_"+cls.name+"__"+v); break; }
catch(ClassRenameFailed e) {}
}
// tell the GC that we don't need this
// class anymore and that it's OK to
// collect it even if its ClassLoader
// is still being used
cls.makeCollectable();
}
static void reload(Class cls) {
// keep around the old name for later reloading
String old_name = cls.getName();
// "unload" by renaming
unload_by_rename(cls);
// old_name now doesn't refer to a
// defined class anymore, and this will
// reload the class file
Class ncls = Class.forName(old_name);
}
The runtime remains internally type consistent.
Of course, the semantics of the loaded code
may not always work out as expected, so
code that used to say
{A a = (A)Class.forName("A");}
may result in a runtime type error.
This means that the sequence:
-- load classes A, B, and C
-- rename class B to B0 and load new version of B
may result in a different internal state than
-- load classes A, new version of B, and C
That's potentially surprising, but not really
a problem given that the "rename" and "reload"
facilities are low-level facilities that a
programming environment uses. But it
indicates that there is something else that
would be useful for a programing environment:
an inquiry function to find out which classes
depend on (or refer to or use) a given class.
With that, all sorts of "reloading" semantics
could be implemented:
static void unload_recursively(Class cls) {
// class may already have been unloaded,
// hence the "try ... catch"
try {unload_by_rename(cls);}
catch(ClassAlreadyUnloaded e) {return;}
// recursively "unload"
Class deps[] = cls.dependentClasses();
for(int i=0;i<deps.length;i++)
unload_recursively(deps[i]);
}
There are several languages and systems
(SML, Erlang, CommonLisp, Smalltalk, ...) that
have addressed incremental development and
dynamic redefinitions, some of them even in
the presence of static typing. I think
it's well worth looking into them more carefully
and see whether similar functionality can't
be provided as part of standard Java.
(Review ID: 34090)
======================================================================
I've been fighting a very mysterious java.lang.IllegalAccessError
between an outer class trying to create an inner class. Know I
know why it happend on every VM I got
(Linux customer , Linux customer , Windows JDK, 1.1 and 1.2): The
outer class was loaden by another class loader than the inner
class was. Is this a reason for throwing a
java.lang.IllegalAccessError?
Because RFE #4151172 has not yet been implemented, I
worked around the class-unloading-problem by giving each loaded
class an individual class loader. If I want a class to completetely
disappear, I drop all references to its very own class loader,
force garbage collection and hope that this class was not used
by any other current class in the system. This worked pretty
well, until I tried to use inner classes.
What behaviour of implementing this feature do you suggest
for current Java releases?
(Review ID: 96654)
======================================================================
|
|
Comments
|
Submitted On 30-JUN-1998
davey
Can't you do all this w/ a custom ClassLoader?
One that you recreate each time you want
to reload a class? Sorry if I missed something
but this is what comes to mind.
Submitted On 12-AUG-1998
vh106
I hardly can say I am just a beginner in JAVA,
but something like this was exactly my problem
while playing around with applets for MSIE3.0
(yuck!) and Netscape (JDK1.02). I don't know
how to use a ClassLoader within the sandbox
while using classes which came with the browser
but which I let interface to my classes. ;) ;(
No problem at all, I was just playing around to
get in touch with JAVA. But I consider it would
be nice if the JAVA API somewhere states that
browsers have to GC and look for class updates
if you hit the reload button.
I also thought of something like a frequently
self modifying JAVA applet (I'm not kidding)
and hated the idea to have to "wrap" it into a
ClassLoader. But that's the way one must do it
cleanly (I am not used to JAVA but I am used to
OOP).
But native support for something like above
would really be nice.
Submitted On 16-NOV-1998
norif
this functionality would be really nice to have.
as noted in the original report, for software
that creates code from a GUI being able to
unload/replace a class makes life a lot easier.
how do java development tools get around this??
if the tool and the generated code are both in
java, does this require 2 JVMs to get working
properly?? (one for the tool, one which is
re-started each time the generated code is
modified??)
trying to get around this with custom class
loaders causes all sorts of problems as
each class loader can only load one class with
a particular name and re-loading classes
with the same name and a different class loader
puts the newly loaded class in a separate name
space (ie. makes it unavailable to already loaded
classes..)
please do something about this.
Submitted On 02-MAR-1999
markgraf
I need a way of loading and unload a whole group of classes at one time, say a
complete package.
I do not need it only for developing, but also for the running: We are writing
a highly modular programm. If someone is not using a particular module, I do
not want to have it consuming memory. On the other hand, if someone starts a
particular module, all the classes should be loaded at once and not one after
another.
All Classes of one module are in one JAR-File so I'd like to be able to load
all classes of a certain JAR-File at once.
If someone knows a way, please do not hasitate to write me an email
(goetz.markgraf@gerling.de).
Submitted On 08-MAR-1999
telamon
Let me start by saying that I'm very grateful that Java language
allows you to extend ClassLoader to perform custom class loading.
It makes my work so much easier.
I'm currently working on a project that involves distributed code
and process migration. In the framework I've designed, all dynamically
loaded software modules conform to a set interface. This interface provides
state capture and stunning (ie pausing the modules current execution
so that it may be checkpointed or migrated).
Now each node in my distributed network has two custom class loaders.
One loads classes from the local file system and the other
from the network. This works great. I start the network up and as
new code is needed by a particular node, it's loaded dynamically.
Now my problem arises when I migrate from one module version to the next.
Loading wise,
I can just force the version number to be part of the classname (ie Foo100,
Foo110, etc...)
That way I can load the new version module and use state capture and stunning
to
migrate from the old version to the new one. However, I'd really like to evict
the
old version of the class from memory. Repeated version migrations would cause
memory leakage. According to the Java specifications, the only way to unload
the old
class would be to destroy the classloader and make a new one.
All the workarounds I've come up with have significant penalties.
1) Have a class loader for each module.
Each node may have anywhere from 5-100 modules
so this isn't particularly desirable.
2) Create a new classloader and migrate every existing
module to use that one.
I really don't want to stun and state capture every other
module on a node just because I need to update one of them.
All I really need is a protected method on ClassLoader
that tells it to dump any references it's holding to a
particular class or sub-package. As I understand it
this will allow the class to eventually be garbage
collected.
I can deal with inheirtance issues in my own
code before I issue the eviction.
I understand the reluctance to add this method due
to the inheirtance issues involved, but I think that
its usefulness would make it worth it.
Submitted On 17-JUL-1999
kW
Please change in http://java.sun.com/docs/books/jls/unloading-rationale.html
"optimization" into a "feature" where explicit
class unloading|renaming|implementation change
is possible. Only with this key feature
will be a JavaOS possible.
Submitted On 15-APR-2000
Shyrakai
I'm no expert on security, but I have tested a solution to this problem that I believe may
be viable. Since the source code for the core classes is publicly available, I downloaded
the source to Java 2 1.3rc2 and took a look at the ClassLoader.java file. There is a private
method therein which appears to add a reference to every new Class to a private
Vector in ClassLoader. Since the method is private in ClassLoader, subclasses can't redefine
it. According to comments, it is called by the VM for the purpose of preventing Classes
from being GC'd as long as their ClassLoader exists.
Now, my solution is predicated upon the notion that, if it's okay to make a Class unloadable by
eliminating its ClassLoader, it ought to be okay to make it unloadable <i>without</i> eliminating
its ClassLoader <u>if</u> specifically requested. My solution only makes a Class unloadable if
all other references have been eliminated, as opposed to the renaming approach mentioned in other
comments (which seems excessive to me).
I modified the ClassLoader source code to include a protected method which simply removes
a given Class from the private Vector. In my derived ClassLoader subclass, I added a public
method called releaseClass which, along with other cleanup specific to my particular subclass,
calls the new method inherited from ClassLoader. I tested this solution, and it worked well.
However, it required modification to a java.lang component, and I believe it is a BIG, BAD, MAJOR
NO-NO to distribute such a change, as it would constitute a corruption of Java. I will not provide
my changes to anybody outside of Sun, and since I work there, I will only provide my changes to
relevant Sun employees internally. I apologize if that seems rather uncharitable, but I'm not
sufficiently well-versed in SCSL legalese to feel comfortable sharing my changes outside of Sun.
If somebody inside Sun is willing to communicate with me about this change (which was only
about 5 lines), whether it is something that could make it into Java or not, I'd appreciate
the contact. Please look me up internally by name.
Mathue M.
mathue@cts.com
Submitted On 13-OCT-2000
golubovsky
Some time ago, when I was experimenting with my own custom
implementation of JVM, I also faced this problem and found
no suitable solution to replace classes within a JVM "on
the fly". A sort of workaround (but requiring serious
changes in JVM concept) would be defining a "classloader
domain" or "virtual submachine". All submachines share
classes (code and static data) loaded by the bootstrap
classloader, but custom classloaders, added to domains,
create private instances of class lookup tables, static
class data, method codes, security stuff, etc. When a
"submachine" is destroyed, all classes loaded into it are
destroyed (i. e. unloaded).
From some point of view, "submachine" is some extension of
"ThreadGroup" (actually I derived it from ThreadGroup
class). Also, an important point, there must be a way to
stop execution of all the threads running within a
"submachine" harmless for the rest "submachines" and the
root JVM, so special resources handling must be in effect.
Another (but conceptually different) solution making "fix-
and-continue" possible might be "JVM fork". Like to UNIX
fork, the entire context of the current JVM is copied into
another JVM which loads necessary application classes,
executes them, and then terminates. The "parent" VM should
have some handle to control its "child" VM being able even
to shutdown it if necessary.
The most "standard" way to simulate this, is just to call
"Runtime.getRuntime().exec("java blah-blah-blah")", but new
JVM invocation is just a time-spending procedure because
all initializations should be performed; JVM fork might be
much faster.
The goal of all my experiments was exactly as described in
other comments here: to make a once-started-up JVM able to
perform all the steps of new application creation (source
compilation, loading, running, recompilation, running
again, etc).
Submitted On 11-JAN-2001
dkf
I *think* you can do this by arranging for code to be loaded
in generations, with a single ClassLoader for each
generation (with the preceding generation as its parent.)
Then you just nuke the CL to get rid of that generation of
code (you should also get rid of later generations at the
same time, of course.) There are problems with this, of
course, since you can't lose earlier classes without losing
later ones, but I'm not convinced you can get around this
restriction securely (or you'd be able to substitute one
class for another inside already loaded code.) Maybe I'm
wrong, but...
Submitted On 22-MAR-2001
int32
I've just put all my votes towards this RFE..
We'r developing a virtual world, with many thousands of
objects (most being a different class), so it would quickly
grow very large if classes aren't unloaded. The developers
also need to be able to update classes while the world is
running. (Hmm rebooting the Java Platform for every change
you make.. that would just be too much like the Windows
Platform!)
Submitted On 01-JUN-2001
dsallings
I think another important thing that I didn't see discussed here is what happens to the instances of a Class that is unloaded. The thing that would be the most useful to me would be a method on Object that is called whenever a class is attempting to unload. I notice problems a lot when developing servlets that maintain shared state. I have pools and shared cache objects which contain cleanup threads and static references to the cache which work most excellently in any environment (servlet or otherwise), but when the classes reload, there's simply no notification. The new class gets loaded and all of the static variables are reset, so I've got no indication that they have run, and the current ones have no idea that they can no longer be referenced. I'm not sure how important this is in regular objects, but I've seen plenty projects stack threads up because they're not aware of how many threads they're using or they have a resource leak on reload.
PLEASE NOTE: JDK6 is formerly known as Project Mustang
|