Controlling Assertions at Runtime
In Chapter 12 you learned about assertions and how they can be turned on or off through command-line options that you pass to the virtual machine (see Section 12.10 on page 300). You can also affect assertion evaluation from running code, although this is rarely required. You will probably need to do this only if you are writing a harness for running other code and must provide your users with options to manage assertions. Such manipulation is done with methods on classloader: public void setdefaultassertionstatus(boolean enabled) Sets the default assertion status for all classes loaded in this loader. Child loaders are not affected, whether they exist at the time this method is invoked or after. The initial default assertion status for a class loader is false. Public void setpackageassertionstatus(String packagename, boolean enabled) Sets the default assertion status for all classes in the given package and it’s subpackages that are loaded in this loader. A null package name means the unnamed package for this class loadersee Chapter 18, page 468.
In all cases the settings only apply to classes that are loaded and initialized in the futureyou cannot change the assertion status of a class once it has been loaded. More specifically, the assertion status of a class is established during initialization: after initialization of the superclass, but before execution of any static initializers.
Objects are created with new, but there is no corresponding delete operation to reclaim the memory used by an object. When you are finished with an object, you simply stop referring to itchange your reference to refer to another object or to null, or return from a method so its local variables no longer exist and hence refer to nothing. Objects that are no longer referenced are termed garbage, and the process of finding and reclaiming these objects is known as garbage collection. The Java virtual machine uses garbage collection both to ensure any referenced object will remain in memory, and to free up memory by deallocating objects that are no longer reachable from references in executing code. This is a strong guaranteean object will not be collected if it can be reached by following a chain of references starting with a root reference, that is, a reference directly accessible from executing code. In simple terms, when an object is no longer reachable from any executable code, the space it occupies can be reclaimed. We use the phrase “can be” because space is reclaimed at the garbage collector’s discretion, usually only if more space is needed or if the collector wants to avoid running out of memory. A program may exit without running out of space or even coming close and so may never need to perform garbage collection. An object is “no longer reachable” when no reference to the object exists in any variable of any currently executing method, nor can a reference to the object be found by starting from such variables and then following each field or array element, and so on.
A Simple Model
Garbage collection is easier to understand with an explicit model so this section describes a simple one, but practical garbage collectors are far more sophisticated. Garbage collection is logically split into two phases: separating live objects from dead objects and then reclaiming the storage of the dead ones. Live objects are those that are reachable from running codethe objects that some action of your code can still potentially use. Dead objects are the garbage that can be reclaimed. One obvious model of garbage collection is reference counting: When object X references object Y, the system increments a counter on Y, and when X drops its reference to Y, the system decrements the counter. When the counter reaches zero, Y is no longer live and can be collected, which will decrement the counts of any other objects to which Y refers. Reference counting fails in the face of cycles, in which loops are created in the references. If X and Y reference each other, neither object’s counter will ever become zero, and so neither X nor Y will ever be collected, nor will anything to which either object refers, directly or indirectly. Most garbage collectors do not use reference counting for this and other reasons.
You should only rarely need to write a finalize method, and when you do, you should write it with great care. If your object has become garbage it is quite possible that other objects to which it refers are also garbage. As garbage, they may have been finalized before your finalize method is invoked and may therefore be in an unexpected state. Garbage collection collects only memory. When you are dealing with non-memory resources that are not reclaimed by garbage collection, finalizers look like a neat solution. For example, open files are usually a limited resource, so closing them when you can is good behavior. But this usually cannot wait until the finalize phase of garbage collection. The code that asks you to perform an operation that opens a file should tell you when it’s donethere is no guarantee that your object holding the open file will be collected before all the open file resources are used up. Still, your objects that allocate external resources could provide a finalize method that cleans them up so that the class doesn’t itself create a resource leak. For example, a class that opens a file to do its work should have some form of close method to close the file, enabling programmers using that class to explicitly manage the number-of-open-files resource. The finalize method can then invoke close. Just don’t rely on this to prevent users of the class from having problems. They might get lucky and have the finalizer executed before they run out of open files, but that is riskyfinalization is a safety-net to be used as a last resort, after the programmer has failed to release the resource manually. If you were to write such a method, it might look like this:
Note also that, in this example, finalize invokes super.finalize in a finally clause. Train yourself so that you always write that invocation in any finalize method you write. If you don’t invoke super.finalize, you may correctly finalize your own part of the object, but the superclass’s part will not get finalized. Invoking super.finalize is one of those good habits you should adopt even when your class doesn’t extend any other class. In addition to being good training, invoking super.finalize in such a case means that you can always add a superclass to a class like processfile without remembering to examine its finalize method for correctness. Invoking the superclass’s finalize method in a finally clause ensures that the superclass’s cleanup will happen even if your cleanup causes an exception. The garbage collector may reclaim objects in any order or it may never reclaim them. Memory resources are reclaimed when the garbage collector thinks the time is appropriate. Not being bound to an ordering guarantee, the garbage collector can operate in whatever manner is most efficient, and that helps minimize the overhead of garbage collection. You can, if necessary, invoke the garbage collector to try to force earlier collection using System.gc or Runtime.gc, as you’ll see in the next section, but there is no guarantee that garbage collection will actually occur.