Chapter 10. Exceptions

  • Run-time Internals: The Heap

  • Garbage Collection

  • Run-time Internals: The Stack

  • Exceptions

  • The Assert Statement

  • Further Reading

  • Some Light Relief—Making an Exception for You

When people say “it's the exception that proves the rule” they mean prove in the sense of “test”, as in proving grounds. They also mean exception in the sense of “infringement”. In other words, you're really hearing “it's the infringements that test whether the rule is any good or not”, but never mind that. In Java, the word “exception” relates to an unwanted error condition, and how we recover from it.

In this chapter, we'll cover the purpose of exceptions, what kinds of things cause them, and how to handle them when they happen. We'll start with a look at two data structures used by the run-time system, because some knowledge of these will give you much better insight into exceptions. We'll end the chapter with a description of the assert statement added to Java in JDK 1.4.

Exceptions and asserts are two ways of dealing with unexpected and unwanted error situations. Exceptions give you a chance to recover from the error and continue program execution. Assert statements provide a way to stop a program dead when it notices some serious inconsistency (like a checksum being incorrect). You can configure at run-time whether you want this “stop on error” behavior or not.

Run-time Internals: The Heap

We made the point in Chapter 2 that the purpose of reference variables is to allow automatic memory management. Many languages allow storage to be allocated dynamically (at run-time). This is good, because it allows a program to adapt to the size of the data it is trying to process, rather than being compiled with a fixed upper limit.

Memory is allocated implicitly by creating an object

In low-level languages like C, the programmer asks for a block of memory with a call to one of the malloc routines. In more structured languages like Java, you get memory by instantiating an object. That object can be an array of arbitrary size, limited only by the maximum value that an int index can hold, or by process size or by the virtual storage on the system.

Memory is a scarce resource, and you should free it up (return it to the system) when it is no longer needed. This can be done by the programmer explicitly managing memory resources, or by the system keeping track of what is in use and freeing up memory that is no longer in use. When the system has responsibility for freeing unused memory the process is known as garbage collection, although garbage recycling would be a more accurate term.

Since its first incarnation as the Oak language, Java has included garbage collection. Automatic garbage collection takes the burden of a challenging and error-prone task away from the programmer, leading to better memory utilization and higher reliability software.

A heap of memory

The heap is the run-time data structure used to support dynamic memory management. It is a large storage area that can allocate memory when the run-time library requests it, and can accept previously allocated memory back for deallocation and returning to the pool. Why is it called a heap? In English, a heap is a quantity of things lying in any old order on top of one another. That's what a heap is in a run-time library. It starts as a large segment of memory given to the process when it starts. As the process runs, it allocates memory, gives it back, allocates some more, and pretty soon you have a heap. The heap itself keeps track of what is in use and what is free. In C, you manage the heap manually. In Java, it is done for you.

Your programs start with a default total heap size, and you can change that maximum using a compiler flag like “ -Xmx64m” for the JDK (“64m” means 64 MB). There are several other compiler flags to give guidance to the heap manager. You would only use them if you are tuning an application to make it fit on hardware that may be slightly too small. Search at java.sun.com for the document “Garbage Collector Ergonomics” if you want more information on these flags.

Storage lifetime

Whenever something is allocated on the heap, its lifetime is independent of the scope in which it was allocated. In other words, it may have been allocated inside a method which has now returned. But if there is still a reference to it outside the method, it is still live.

 int[] saveIt;

 void foo() {
     int i;
     int[] a = new int[1000000];
     saveIt = a;
 }

/*more code*/
  foo();
  saveIt[1000] = 23; // is it valid? array was allocated in foo
                     // and foo is no longer live

In the code above, an int array is allocated in a method and a reference to that array is stashed away for later use (the “saveIt=a” statement). Later, after the method has returned, the saved reference is used to access the array.

This is perfectly valid for languages like Java that use heap-based storage. It is a potent source of bugs for languages like C that allocate local variables on the stack. That part of the stack is freely overwritten after the flow of control returns from a method call. If the run-time allocated an array there, the array can be freely overwritten with other values.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
13.59.107.152