Java in a Nutshell, 5th Edition [Electronic resources] نسخه متنی

اینجــــا یک کتابخانه دیجیتالی است

با بیش از 100000 منبع الکترونیکی رایگان به زبان فارسی ، عربی و انگلیسی

Java in a Nutshell, 5th Edition [Electronic resources] - نسخه متنی

| نمايش فراداده ، افزودن یک نقد و بررسی
افزودن به کتابخانه شخصی
ارسال به دوستان
جستجو در متن کتاب
بیشتر
تنظیمات قلم

فونت

اندازه قلم

+ - پیش فرض

حالت نمایش

روز نیمروز شب
جستجو در لغت نامه
بیشتر
لیست موضوعات
توضیحات
افزودن یادداشت جدید


3.4. Destroying and Finalizing Objects


Now that we've seen
how new objects are created and initialized in Java, we need to study
the other end of the object life cycle and examine how objects are
finalized and destroyed.

Finalization is the
opposite of initialization.

In Java,
the memory occupied by an object is automatically reclaimed when the
object is no longer needed. This is done through a process known as

garbage collection . Garbage collection is a
technique that has been around for years in languages such as Lisp.
It takes some getting used to for programmers accustomed to such
languages as C
and C++, in which you must call the free()
function or the delete operator to reclaim memory.
The fact that you don't need to remember to destroy
every object you create is one of the features that makes Java a
pleasant language to work with. It is also one of the features that
makes programs written in Java less prone to bugs than those written
in languages that don't support automatic garbage
collection.


3.4.1. Garbage Collection


The
Java interpreter knows exactly what
objects and arrays it has allocated. It can also figure out which
local variables refer to which objects and arrays and which objects
and arrays refer to which other objects and arrays. Thus, the
interpreter is able to determine when an allocated object is no
longer referred to by any other active object or variable. When the
interpreter finds such an object, it knows it can safely reclaim the
object's memory and does so. The garbage collector
can also detect and destroy cycles of objects that refer to each
other, but are not referenced by any other active objects. Any such
cycles are also reclaimed.

Different VM implementations handle garbage collection in different
ways. It is reasonable, however, to imagine the garbage collector
running as a low-priority background thread, so it does most of its
work when nothing else is going on, such as during idle time while
waiting for user input. The only time the garbage collector must run
while something high-priority is going on (i.e., the only time it
actually slows down the system) is when available memory has become
dangerously low. This doesn't happen very often
because the low-priority thread cleans things up in the background.


3.4.2. Memory Leaks in Java


The fact
that Java supports garbage collection dramatically reduces the
incidence of a class of bugs known as

memory
leaks . A memory leak occurs when memory is allocated and
never reclaimed. At first glance, it might seem that garbage
collection prevents all memory leaks because it reclaims all unused
objects. A memory leak can still occur in Java, however, if a valid
(but unused) reference to an unused object is left hanging around.
For example, when a method runs for a long time (or forever), the
local variables in that method can retain object references much
longer than they are actually required. The following code
illustrates:

public static void main(String args[]) {
int big_array[] = new int[100000];
// Do some computations with big_array and get a result.
int result = compute(big_array);
// We no longer need big_array. It will get garbage collected when there
// are no more references to it. Since big_array is a local variable,
// it refers to the array until this method returns. But this method
// doesn't return. So we've got to explicitly get rid of the reference
// ourselves, so the garbage collector knows it can reclaim the array.
big_array = null;
// Loop forever, handling the user's input
for(;;) handle_input(result);
}

Memory leaks can also occur when you use
a hash table or similar data structure to associate one object with
another. Even when neither object is required anymore, the
association remains in the hash table, preventing the objects from
being reclaimed until the hash table itself is reclaimed. If the hash
table has a substantially longer lifetime than the objects it holds,
this can cause memory leaks.

The key to avoiding memory leaks is to set object references to
null when they are no longer needed if the object
that contains those references is going to continue to exist. One
common source of leaks is in data structures in which an
Object array is used to represent a collection of
objects. It is common to use a separate size field
to keep track of which elements of the array are currently valid.
When removing an object from the collection, it is not sufficient to
simply decrement this size field: you must also
set the appropriate array element to null so that
the obsolete object reference does not live on.


3.4.3. Object Finalization


A

finalizer in Java is
the opposite of a constructor. While a constructor method performs
initialization for an object, a finalizer method can be used to
perform cleanup or "finalization"
for the object. Garbage collection automatically frees up the memory
resources used by objects, but objects can hold other kinds of
resources, such as open files and
network
connections. The garbage collector cannot free these resources for
you, so you may occasionally want to write a finalizer method for any
object that needs to perform such tasks as closing files, terminating
network connections, deleting
temporary files, and so on. This is particularly true for classes
that use native methods: these classes may need a
native finalizer to release native resources
(including memory) that are not under the control of the Java garbage
collector.

A finalizer is an instance method that
takes no arguments and returns no value. There can be only one
finalizer per class, and it must be named
finalize().[3] A finalizer can throw any kind of
exception or error, but when a
finalizer is automatically invoked by the garbage collector, any
exception or error it throws is ignored and serves only to cause the
finalizer method to return. Finalizer methods are typically declared
protected (which we have not discussed yet) but
can also be declared public. An example finalizer
looks like this:

[3] C++ programmers should note that although Java constructor
methods are named like C++ constructors, Java finalization methods
are not named like C++ destructor methods. As we will see, they do
not behave quite like C++ destructor methods either.


protected void finalize() throws Throwable {
// Invoke the finalizer of our superclass
// We haven't discussed superclasses or this syntax yet
super.finalize();
// Delete a temporary file we were using
// If the file doesn't exist or tempfile is null, this can throw
// an exception, but that exception is ignored.
tempfile.delete();
}

Here are some important points about finalizers:

  • If an object has a finalizer, the finalizer method is invoked
    sometime after the object becomes unused (or unreachable), but before
    the garbage collector reclaims the object.

  • Java makes no guarantees about when garbage collection will occur or
    in what order objects will be collected. Therefore, Java can make no
    guarantees about when (or even whether) a finalizer will be invoked,
    in what order finalizers will be invoked, or what thread will execute
    finalizers.

  • The Java interpreter can exit without garbage collecting all
    outstanding objects, so some finalizers may never be invoked. In this
    case, resources such as network connections are closed and reclaimed
    by the operating system. Note, however, that if a finalizer that
    deletes a file does not run, that file will not be deleted by the
    operating system.

  • To ensure that certain actions are taken before the VM exits, Java
    1.1 provided the Runtime method
    runFinalizersOnExit(). Unfortunately, however,
    this method can cause deadlock and is inherently unsafe; it was
    deprecated in 1.2. In Java 1.3 and later, the
    Runtime method
    addShutdownHook()
    can safely execute arbitrary code before the Java interpreter exits.

  • After a finalizer is invoked, objects
    are not freed right away. This is because a finalizer method can
    resurrect an object by storing the this pointer
    somewhere so that the object once again has references. Thus, after
    finalize() is called, the garbage collector must
    once again determine that the object is unreferenced before it can
    garbage-collect it. However, even if an object is resurrected, the
    finalizer method is never invoked more than once. Resurrecting an
    object is never a useful thing to dojust a strange quirk of
    object finalization.

  • The finalize( ) method is an instance method, and
    finalizers act on instances. There is no equivalent mechanism for
    finalizing a class.


In practice, it is quite rare for an application-level class to
require a finalize( ) method. Finalizer methods
are more useful, however, when writing Java classes that interface to
native platform code with native methods. In this
case, the native implementation can allocate memory or other
resources that are not under the control of the Java garbage
collector and need to be reclaimed explicitly by a
native finalize() method.

Furthermore, because of the uncertainty about when and whether a
finalizer runs, it is best to avoid dependence on finalizers. For
example, a class that includes a reference to a network socket should
define a public close() method, which calls the
close( ) method of the socket. This way, when the
user of your class is done with it, she can call close(
)
and be sure that the network connection is closed. You
might, however, define a finalize( ) method as
backup in case the user of your class forgets to call close(
)
and allows an unclosed instance to be garbage-collected.


/ 1191