Lab 7: Finalizers and String Interning

In this lab you will learn two new dark corners of Java. The first is just for fun (it would take a very specialized application to make good use of it); the second reveals a little more information about how Java handles Strings.

1. Setup

Make a new directory for this lab and copy the the files from the course directory for this lab.

cp /Public/cs245/lab7/* .

2. Finalizers

The general rule here is to prefer the standard. Finalize, for instance, is not standard; it is special, and it is a particularly fuzzy and silly word. Does it mean "terminate," or does it mean "put into final form"? One can't be sure, really, what it means, and one gets the impression that the person using it doesn't know, either, and doesn't want to know.
--- Strunk and White, The Elements of Style

a. Observation

Open the files FTest.java and FNumber.java and do a quick look-over. FNumber is just a wrapper object for an integer value (essentially a stripped down version of the standard class Integer). FTest is a driver program that allows users to enter numbers to add to an ArrayList of these FNumbers, and also to delete them. Each time it displays the list as it currently stands. Compile and run FTest. To add a number, type "a 14" (for example); to delete that same number, type "d 14". (I've tried to catch all the exceptions that could happen if you type incorrect commands, but I may have missed some; try not to type incorrect input anyway.)

What do you observe when you delete an element? What could be triggering that message? Take a look at FNumber's superclass, FObject. All it has is a method called finalize().

You have probably heard that Java is a garbage collected language. This means that when an object is no longer refered to by anything else in the system, the memory it takes up is marked as being available for another use. (When we use dynamic memory in in C later in this semester, we'll see what an inconvenience it is to have to free up memory we don't need anymore, since C is not garbage-collected.)

finalize() is a method that is automatically (sort of) called when an object is about to be garbage-collected. The class Object has a finalize() method which does nothing; other classes can override it. finalize() methods are often called finalizers. Essentially they allow objects to make a last request of sorts, to do one last thing before they are recycled.

There are several strange things about finalizers. For one thing, you never know when the JVM is going to collect garbage---anyone writing a Java virtual machine can pick a GC strategy they think is going to be optimal (and there has been tons of research into that). We've cheated a little in this example by making FTest "force" a garbage collection by calling the method System.gc()

Also notice that the finalizer seems to be running during the printing of the list (if you don't notice this, try making a really big list-- 10 or more elements-- and then deleting something). This is because the finalizer is run in a separate thread from the main program (a thread is just a control process that is part of a larger program).

b. The Marvin K. Mooney object

One interesting consequence of finalizers is that they give an object that's about to be garbage collected an oppportunity to save itself. It won't be recycled as long as someone has a reference to it, so the object simply needs to get some other object to pay attention to it.

Write a finalizer for FNumber (this will override the one inherited from FObject; in fact, FNumber doesn't need to extend FObject anymore) that puts FNumber back into the list. Notice that list is public in FTest. Effectively the object should refuse to be deleted from the list.

Test your revision. What do you observe?

c. Outsmarting the exception

If you don't get an exception, keep trying until you do (again, you may have to do this on a large list). The problem is that, just as the old finalizer ran during the printing of the list, so is the new one. That's bad because for an Iterator to work right, it assumes that there will be no changes to the list while it is iterating--except possibly changes that it makes itself.

The best we can do is change the code in FTest that prints the list. Instead do the following:

Test your code. Once it appears to be working (that is, you try to remove an object, but the object is still in the list), try to remove the same object a second time. What do you observe?

d. Leaving a legacy

The reason why the object actually gets removed the second time you try to delete it is because finalizers get called only once for each object. Next time the object is about to be garbage collected, the garbage collector notices that it has already been finalized, and doesn't call the method again. It got a second chance, but no third chance.

The object can't put itself back in to the list a second time, but there way to make it seem like it can. Re-write the finalizer to that it will always look like an object that is removed appears back in the list (remember, the only way a user can identify a FNumber object is by its value).

Test your new finalizer. What havoc can you wreak by having your finalizer add several objects to the list?

e. Why?

What possible use could a finalizer have? Mainly this feature is there in case an object is holding a system resource that needs to be cleaned up and released. For example, suppose an object has a file open, and it's writing to the file as it does its operations (maybe it's logging events, for debugging purposes). The object might need to write some final information to the file and close the file.

3. String interning

a. When == works better than it should

Early on in Java instruction, we tell students they must always use the equals() method to compare Strings because == just compares the memory location (all of which is true). However, the instructor then hopes a students doesn't go off and experiment and find results that don't seem to fit.

Look at the file StringSurprise.java, predict what its output should be, and then compile and run it. What do you observe?

b. The pool of Strings

The String class maintains a list of "canonical representations" of all String objects that are created during the run of the program. This means if there are more than one string with identical content, one of those Strings is kept in this private list in the String class. You can retreive a string's canonical feature with the method intern(). To "intern" a string means to get a reference to its canonical represenation to use instead.

Read the description of the intern() method in the Java API and then explain why StringSurprise works the way it does--- notice in particular what the API says about "All literal strings...".

c. A String game

To make sure you caught all that, look at the class StringGame. Predict what the output should be. Then compile and run, and see how good your prediction is.

4. To turn in

Turn in a hard copy of your FNumber.java class.


Thomas VanDrunen
Last modified: Thu Oct 18 11:49:05 CDT 2007