A big practical advantage of programming in Java over
C, C++ and many other languages involves Java's much simpler memory
management. In fact, the Java programmer can leave most of the memory
management up to the internal Garbage Collector (GC).
The GC runs in a thread (a parallel process, see Chapter
8) in the JVM. When the free memory drops below a threshold
the GC begins to run to eliminate unneeded objects. Exactly when
it runs and for how long, however, cannot be controlled by the user
program.
Note: This unpredictability
of the GC, in fact, causes serious problems for real-time
(RT) applications of Java. It violates the deterministic requirements
of a RT system in which the start time and duration of a process
must be explicitly known within definite tolerances. A special
set of specifications have been developed for RT Java and the
GC is a major focus. The JVM does not actually require a GC and
in some special situations, the GC is simply turned off or left
out. In that case, the program must itself make sure that it does
not run out of memory. In Chapter
24 : Embedded Java we discuss how some Java hardware processors,
such as the Parallax
Javelin, use this approach.
If a section of memory previously used by an object
is no longer referenced by any variable, the garbage collector will
release this memory for re-use by new objects. This takes place
automatically without any input from the user program. There are
no heap commands such malloc()
and free()
functions in C needed to allocate or de-allocate memory buffers.
Java does use a similar "new"
operator as in C++ for creating objects but has no "delete"
operator.
GC Designs
The JVM Specification does not in fact specify very much about
the GC. It leaves considerable room for variation in implementation.
Research continues in the computer science community for fast
and efficient GC algorithms. A GC must meet a number of difficult
challenges:
- Recognize and eliminate dead (unreferenced) objects from the
heap
- Keep the heap from becoming so fragmented that new objects
don't easily fit.
- Minimize the processing time taken away from the application
- Recognize circular cases where dead objects (that is, objects
no longer accessible from the live thread processes or from static
references) referencing each other and so might give them the
appearance of life.
Some of the most common GC designs include (Ref.1)
- Copying Collector
Scan through all references to determine which objects are live
(referenced) and copy them to one half of the heap. When finished,
free up the old half. Restart the application. Repeat when free
memory drops below a threshold.
- Mark & Sweep
Scan through all references and mark the objects that are live.
Free up the areas occupied by dead objects.
- Mark & Compact
Similar to Mark & Sweep except after the marking phase,
the live objects are copied to a free area and all of the area
previously occupied by objects is freed up.
- Generational
Most objects in most programs only last a brief time. It is more
efficient to concentrate on freeing up memory from the short-lived
objects and spend as little time as possible checking on old but
stable objects. The generational GC creates at least two sub-heap
areas with one for short-lived objects and the other for old objects.
A Mark & Sweep or other GC algorithm would work on the short-lived
heap. Those objects that last beyond a few cycles are moved to
the long-lived heap, which is only checked occasionally.
These types of algorithms must interrupt the application threads
and complete their job before returning control. There are some
algorithms that work in an incremental approach, rather than completing
a full clean-up at one go, and thus interfere less with the program.
Memory Leaks
Memory leak refers to the loss of free memory in the heap
to unusable data as a program runs. This can lead to inefficient
memory utilization and eventually to a crash of the program when
no free memory remains. Even with the garbage collector, memory
leaks can still occur in Java.
Exampes of situations that create memory leaks include (ref.2)
- Maintaining references to unneeded
objects
For example, a method might create temporary objects in the first
section of the method and then go on to do other tasks such as
a loop in which the temporary objects are no longer needed. If
references to the temporary objects remain, then the objects will
not be collected and take up memory. In such cases the references
should explicitly be set to null.
- Waiting for finalizer
A class can override
the finalize()
method and use it to do clean up tasks before the object
is reclaimed by the GC. However, there is no way to know when
the finalizer will be called and if a lot of objects with finalizers
are waiting for their final invocation, a lot of memory might
be used up. Generally, it is recommended to avoid use of the finalize()
method and instead create a clean up method that you call
explicitly when the object is no longer needed.
References & Web
Resources
- Nagendra Nagarajayya and J. Steven Mayer,
Improving
Java Application Performance and Scalability by Reducing Garbage
Collection Times and Sizing Memory , July 2002, at java.sun.com.
- Steve Wilson , Jeff Kesselman The
Truth About Garbage Collection at JavaTM
Platform Performance Strategies and Tactics, 2001, Sun Microsystems,Inc.
Latest update: June 16, 2005
|