I am trying to get rid of some memory leaks. I'd like to reset all the static variables of all the classes (not only mine) from a class loader. There is a classes attribute that lists all the classes known by the ClassLoader.
So I just want to loop over it and with reflection set the static variables to null.
The problem is that all those classes have not necessarily been initialized (the Static Block Initialization did not run). As the purpose is to reset the values and then unload the classes, there is no point initializing this classes. Moreover, when I reset a class ROOT that is used in the SBI of another class CHILD, running the SBI of CHILD can lead to unexpected behavior...
So the question is: Is there a way to know if the SBI has been run by the JVM or not.
Note:
to anybody proposing to use the findLoadedClass of the ClassLoader, there is in the specification this important sentence: *In this post, there is an important note: "loaded" doesn't mean "initialized". initialization only happens at precise moments defined by JLS3 $12.4.1 *
static block was initialized if class was loaded on JVM.
Is class loaded on JVM you can detect with ClassLoader
Related
I noticed that we can set a Thread's context classloader at will. Does it mean that every new results in the context classloader's loadClass getting called?
No, loadClass() will only be invoked once, the first time the class is accessed. (This will not necessarily happen on new(), it may happen if you try to access a static member of the class.)
In every subsequent access of the class, loadClass() will not be invoked.
That's because every time the class is needed, the ClassLoader invokes findClass() internally, which tries to find an already loaded class, so if the class has already been loaded, the ClassLoader refrains from invoking loadClass() again.
Class loading by ClassLoaders can be implemented to eagerly load a class as soon as another class references it or lazy load the class until a need of class initialization occurs but you should be aware that the behavior might not exactly the same as new in some scenarios and might be the same in others. If Class is loaded before its actually being used it can sit inside before being initialized. this might vary from JVM to JVM, but its guaranteed by JLS that a class will be loaded when there is a need of static initialization.
as also explained in What does "new" do in Java w.r.t. class loader?:
Class loading is performed only once for a given namespace, unless the
Class in question has been previously unloaded. Therefore, the
equivalent expression A.class.getClassLoader().loadClass("B's
canonical name") will be executed only once in most scenarios. In
other words, if you have two expressions - new A(), the loadClass will
be performed only once.
I am writing code that adds functions to a 'mod' if it exists in the classpath (referenced by pixelmonPresent)
PixelHammerTool extends ItemHammer
, ItemHammer only exists if pixelmon is present
The issue im having is, if i do this in the class (same package)
if(Basemod.pixelmonPresent) {
rubyHammer = new PixelHammerTool(Basemod.RUBY, "pixelutilitys:RubyHammer", "rubyHammer");
}
It will cause a class not found on PixelHammerTool,
Why is this being called if the if statement is false and what can i do about it ?
The why is simple and straightforward: because when a class is loaded, all the classes referenced by it are loaded too. (In fact they are loaded first.)
Avoiding it isn't too complicated either, although the code won't look nice: you need to load the class with reflection, using Class.forName(), find the constructor you want from the array returned by Class.getConstructors() and then create an instance using Constructor.newInstance().
Note that while if it only happens a few times in your code, this solution is fine, if you find yourself doing this a lot then you should probably look for a dependency injection framework that will do the heavy lifting for you.
Under the Linking section in the specs, we see this:
For example, a Java Virtual Machine implementation may choose to resolve each symbolic reference in a class or interface individually when it is used ("lazy" or "late" resolution), or to resolve them all at once when the class is being verified ("eager" or "static" resolution). This means that the resolution process may continue, in some implementations, after a class or interface has been initialized. Whichever strategy is followed, any error detected during resolution must be thrown at a point in the program that (directly or indirectly) uses a symbolic reference to the class or interface.
So when the constant has to be defined is implementation-dependent, based on the class loader. The behavior you're seeing is consistent with the "eager" resolution mentioned: when you reference PixelHammerTool in your code, even if it's for a runtime path that will never be hit, the class loader tries to link in its definition, which does not exist.
This strategy causes the JVM to start slower but execute faster at runtime, which is generally the strategy taken in all the implementations I'm familiar with. Indeed, the default class loader is given the name "bootstrap class loader" because it has this behavior - load classes at JVM bootstrap time.
You can either instantiate the class via reflection, as biziclop suggested (the easier route), which forces linking at runtime, or find or create a class loader that instantiates classes lazily.
I have a unit test class which when ran individually passes. When I run it with all the other tests within the package it fails with the failure being that an independent static class has not been initialized. This independent static class is used by other classes and so it seems that its state is maintained between tests.
Is my observation correct or is something else happening? Also it would be good if someone could provide a link or something as reference.
Thanks!
This independent static class is used by other classes and so it seems that its state is maintained between tests.
Yes. That is what will happen. And this is just one of the reasons that statics are awkward.
Static fields exist for the lifetime of the classes that define them, and that generally means for the lifetime of the JVM. I tried to find a place in the JLS where this is stated explicitly. The closest I could find was JLS 8.3.1.1 which says:
"If a field is declared static, there exists exactly one incarnation of the field, no matter how many instances (possibly zero) of the class may eventually be created. A static field, sometimes called a class variable, is incarnated when the class is initialized (§12.4)."
Elsewhere the JLS says that a class is initialized only once.
The exception is when a class gets unloaded, but that won't happen here. Or at least, not with normal / default JUnit framework behaviour. (But it can be done: see Using different classloaders for different JUnit tests?)
And in case you are worried, there isn't any JUnit "magic" to reset the statics to their initial state (however you defines that). It is too complicated (and horrible) to contemplate implementing that.
Methods have no state (except while a given method is running, of course), so none is saved between invocations — even for static methods..
Any static field saves its state for the duration of the JVM's execution (unless code changes its value, of course). JUnit uses one JVM for all of its tests, so, yes, static fields save state between tests.
That's one of the biggest reason that people recommend not using static fields where it's possible to avoid them: it reduces the amount of global state you have to worry about, and thus makes it much easier to reason about tests.
When a program written in Java is running, will all of its classes be all loaded into the main memory? If so, isn't it a waste of RAM?
No it's fine, because of virtual address space and virtual memory. Read these:
http://en.wikipedia.org/wiki/Virtual_memory
http://en.wikipedia.org/wiki/Virtual_address_space
Virtual memory means that you can load a large amount into memory and the unused sections are saved to disc and are moved out of physical RAM.
Virtual address space means that each process (one example of a process is your Java program) has its own address space, so it does not 'steal' addresses from other processes.
Only classes that are referenced during a particular execution are loaded. Most large Java programs will frequently run with many of the classes not loaded as those classes serve various scenarios not exercised by that particular process.
Classes in the standard library are handled the same as application classes. For instance, if your application does not reference AWT, no classes in AWT packages will be loaded.
Java language spec contains the wording which explicitly precludes eager initialization of classes.
JLS Section 12.4:
A class or interface type T will be
initialized immediately before the
first occurrence of any one of the
following:
T is a class and an instance of T is
created.
T is a class and a static
method declared by T is invoked.
A
static field declared by T is
assigned.
A static field declared by T
is used and the field is not a
constant variable (§4.12.4).
T is a
top-level class, and an assert
statement (§14.10) lexically nested
Note my use of the term "initialization". A class is initialized as part of constructing Class object, when parsing the binary data that defines the class.
There is nothing precluding a particular ClassLoader implementation from loading the binaries of all the classes that it sees into memory, but it cannot fully load those classes until they are requested without violating JLS.
For a common ClassLoader implementation, see URLClassLoader.
I'm having a strange problem that I can't figure out that popped up when trying to pluginize my program. An additional problem is that I'm not able to create a simple test case, because every time I try it works. There must be some complication I'm missing. But I'll try to describe the situation as clearly as possible, in case it sounds familiar to anyone.
I have a base class called Seed which is part of the main application and loaded by the system classloader. I have a plugin which contains a class Road which is a subclass of Seed. It is loaded at runtime from a separate jar file. The class Road references the field Seed.garden, which is defined as:
protected final Garden garden;
Note that I don't get compilation errors. I also don't get runtime errors when the plugin jar is included on the system classpath. Only when my main application loads the plugin using a new classloader (that has the system classloader as its parent) do I get the error. The error is:
java.lang.IllegalAccessError: tried to access field package.Seed.garden from class package.Road$4
It must have something to do with the fact that the subclass has been loaded by a different class loader than the superclass, but I can't find any official reason why that shouldn't work. Also, like I said, when I try to reproduce the problem with a simple test case (that includes the separate jars, loading the subclass with a different classloader, etc.), I don't get the error.
It also doesn't seem likely that I'm violating the access rules since it works when the classes are loaded by the same classloader, and I don't get compilation errors.
I'm out of ideas! Does anyone recognise this problem, or have some pointers for me for directions in which to look? Help!
OK, so with the help of axtavt and other respondents I figured out what the problem is. The other answers helped, but they didn't get it exactly right, which is why I'm answering my own question. The problem turned out to be the concept of "runtime packages", defined in the Java Virtual Machine specification as follows:
5.3 Creation and Loading
...
At run time, a class or interface is determined not by its name alone, but by a pair: its fully qualified name and its defining class loader. Each such class or interface belongs to a single runtime package. The runtime package of a class or interface is determined by the package name and defining class loader of the class or interface.
...
5.4.4 Access Control
...
A field or method R is accessible to a class or interface D if and only if any of the following conditions is true: ...
R is protected and is declared in a class C, and D is either a subclass of C or C itself.
R is either protected or package private (that is, neither public nor protected nor private), and is declared by a class in the same runtime package as D.
The first clause explains why Road is allowed to access Seed.garden, since Road is a subclass of Seed, and the second clause explains why Road$4 is not allowed to access it, despite being in the same package as Road, since it is not in the same runtime package, having been loaded by a different class loader. The restriction is not actually a Java language restriction, it is a Java VM restriction.
So the conclusion for my situation is that the exception occurs due to a legitimate restriction of the Java VM, and I'm going to have to work around it, probably by making the fields public, which is not a problem in this case since they are final, and not secret, or perhaps by exporting Seed.garden to Road$4 via Road, which does have access.
Thank you everyone for your suggestions and answers!
Sounds like you have a class identity crisis, having two different class loaders loading the same class in the class hierarchy or similar. Read up some on the java class loaders. Here is a good one for introduction, for "class identity crisis" see figure 2: http://www.ibm.com/developerworks/java/library/j-dyn0429/
I should add that Road$4 is an anonymous inner class of Road...
Someone else thought this was a bug as well back in 1998:
http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4116802
An inner class has no greater access to members of another class than
a top-level class, except for those members that are declared within
an enclosing, enclosed, or sibling class. It is a common misconception
that an inner class has unrestricted access to inherited members of
its enclosing classes. This is not true.
I would probably research that fact a bit more though because this was reported originally against Java 1.2, but I seem to remember from my reading that this is true today as well.
EDIT:
I confirmed this to be true:
http://docs.oracle.com/javase/tutorial/java/javaOO/summarynested.html
The scope for an anonymous inner class is only the point where it is defined. So it will not have access to inherited members, even if the outer class does.
This is permission error, so it depends on the framework you use to run your runtime.
Just to clarify this is indeed this, make the parent member public, and then try to run. In case everything is ok, then restore your code, and according to the runtime you use we need to configure the correct security access.