The Artima Developer Community
Sponsored Link

Java Community News
When GC is Not Enough: Resource Management in Java

10 replies on 1 page. Most recent reply: Mar 30, 2006 1:41 PM by Morgan Conrad

Welcome Guest
  Sign In

Go back to the topic listing  Back to Topic List Click to reply to this topic  Reply to this Topic Click to search messages in this forum  Search Forum Click for a threaded view of the topic  Threaded View   
Previous Topic   Next Topic
Flat View: This topic has 10 replies on 1 page
Frank Sommers

Posts: 2642
Nickname: fsommers
Registered: Jan, 2002

When GC is Not Enough: Resource Management in Java Posted: Mar 28, 2006 10:52 AM
Reply to this message Reply
Summary
In a recent article, Brian Goetz differentiates between memory and non-memory resources used by a Java application, and shows that garbage collection does not handle non-memory resources. Such resources must be managed by the programmer, and Brian demonstrates a few strategies for how to do this well.
Advertisement
While built-in automatic garbage collection is among the chief reasons for Java's phenomenal success, thinking that GC allows a developer to give no thought to resource management is misleading, and results in faulty applications. In a recent article, Brian Goetz points out that:

The vast majority of resources used in Java programs are objects, and garbage collection does a fine job of cleaning them up. Go ahead, use as many Strings as you want. The garbage collector eventually figures out when they've outlived their usefulness, with no help from you, and reclaims the memory they used.

On the other hand, nonmemory resources like file handles and socket handles must be explicitly released by the program, using methods with names like close(), destroy(), shutdown(), or release().

Brian then divides such nonmemory resources based on the span of time such a resource is used: Those used within a method, and those with arbitrary life-cycles.

Most resources are not held for the lifetime of the application; instead, they are acquired for the lifetime of an activity.

We all know that we should use finally to release heavyweight objects like database connections, but we're not always so careful about using it to close streams[...]. It's also easy to forget to use finally when the code that uses the resource doesn't throw checked exceptions.

As Brian shows, correctly releasing resources often leads to unwieldy Java code, but there is not much a developer can do to simplify this. This is a matter of correct coding, and there is no magic that can flatten out code blocks such as this:
Statement statement = null;
ResultSet resultSet = null;
Connection connection = getConnection();
   try {
      statement = connection.createStatement();
      resultSet = statement.executeQuery("SELECT * FROM Bar");
     // Use resultSet
   }
   finally {
     try {
        if (resultSet != null)
             resultSet.close();
        }
     finally {
        try {
            if (statement != null)
                statement.close();
            }
        finally {
            connection.close();
        }
     }
 }
As for managing resources with arbitrary lifecycles:

We're back to where we were with C -- managing resource lifecycles manually. In a server application where clients make a persistent network connection to the server for the duration of a session (like a multiplayer game server), any resources acquired on a per-user basis (including the socket connection) must be released when the user logs out.

[...] Resources with arbitrary lifecycles are almost certainly going to be stored in [...] a global collection somewhere. To avoid resource leaks, it is therefore critical to identify when the resource is no longer needed and remove it from this global collection.

One especially useful piece of advice relates to resource ownership:

A key technique for ensuring timely resource release is to maintain a strict hierarchy of ownership; with ownership comes the responsibility to release the resource. If an application creates a thread pool and the thread pool creates threads, the threads are resources that must be released (allowed to terminate) before the program can exit.

But the application doesn't own the threads; the thread pool does, and therefore the thread pool must take responsibility for releasing them. Of course, it can't release them until the thread pool itself is released by the application.

What are your strategies for managing nonmemory resources in Java? Do you know of any good techniques to ease resource management chores?


Randy Rizun

Posts: 2
Nickname: rrizun
Registered: Mar, 2006

Re: When GC is Not Enough: Resource Management in Java Posted: Mar 28, 2006 11:01 PM
Reply to this message Reply
Couldn't the code snippet be made more concise like this:

Connection connection = getConnection();
try {
 Statement statement = connection.createStatement();
 try {
  ResultSet rs = statement.executeQuery("...");
  try {
   // ... use rs
  } finally {
   rs.close();
  }
 } finally {
  statement.close();
 }
} finally {
 connection.close();
}


But, ya, in general I don't think there is any magic way to simplify heavy resource management in Java...

Laurent Simon

Posts: 1
Nickname: stratic
Registered: Mar, 2006

Re: When GC is Not Enough: Resource Management in Java Posted: Mar 29, 2006 4:57 AM
Reply to this message Reply
To be really safe, resources management must be done using the following pattern like a dumb rule:

final AClass resource =  allocateResource();
try {
    // ...
    // resource usage
    // ...
}
finally {
    resource.release();
}

Bill Venners

Posts: 2248
Nickname: bv
Registered: Jan, 2002

Re: When GC is Not Enough: Resource Management in Java Posted: Mar 29, 2006 11:12 AM
Reply to this message Reply
> To be really safe, resources management must be done using
> the following pattern like a dumb rule:
>
>
> final AClass resource =  allocateResource();
> try {
>     // ...
>     // resource usage
>     // ...
> }
> finally {
>     resource.release();
> }
> 


The example in Frank's original post was a quote from Brian's article, and I don't really like the look of it. The code you show here is the idiom I always use (I consider it the Java idiom), unless some other idiom is called for by the API. One place where this happened to us is in Hibernate, where the recommended idiom was to do something like:

Session session = SessionMaker.currentSession();
Transaction txn = null;
try {
    txn = session.beginTransaction();
 
    // Do stuff
 
    txn.commit();
}
catch (Exception e) {
    try {
        if (txn != null) {
            txn.rollback();
        }
    }
    catch (HibernateException e1) {
        // Deal with inability to roll back
    }
    finally {
       throw e; 
    }
}
finally {
    SessionMaker.closeSession();
}


It kind of bugged me that the Hibernate folks recommended an idiom that departed from the usual java idiom, but we follow it.

Achilleas Margaritis

Posts: 674
Nickname: achilleas
Registered: Feb, 2005

Re: When GC is Not Enough: Resource Management in Java Posted: Mar 29, 2006 11:35 AM
Reply to this message Reply
The programming language D manages resources correctly: it uses GC for memory and RAII for other resources.

The real difference between memory and other resources is that memory references make up a complicated graph, where as other resources do not...hence the difference.

Roland Pibinger

Posts: 93
Nickname: rp123
Registered: Jan, 2006

Re: When GC is Not Enough: Resource Management in Java Posted: Mar 29, 2006 1:43 PM
Reply to this message Reply
> To be really safe, resources management must be done using
> the following pattern like a dumb rule:
>
> final AClass resource =  allocateResource();
> try {
>     // ...
>     // resource usage
>     // ...
> }
> finally {
>     resource.release();
> }
> 


Agreed. You have to release a resource in 'finally'. But the point in Frank Sommers' example also is that you need nested try/finally blocks to release more than one resource.

You can factor out the cleanup code into a reusable function (Frank Sommers' example):

Statement statement = null;
ResultSet resultSet = null;
Connection connection = getConnection();
try {
  statement = connection.createStatement();
  resultSet = statement.executeQuery("SELECT * FROM Bar");
   // Use resultSet
}
finally {
  close (connection, statement, resultSet);
}


the reusable cleanup function:
void close (Connection connection, Statement statement, ResultSet resultSet) {
  try {
    if (resultSet != null)
      resultSet.close();
  }
  finally {
    try {
      if (statement != null)
        statement.close();
      }
    finally {
      if (connection != null)  
        connection.close();
    }
  }
}

You may also provide exception translation in the close() function.

Morgan Conrad

Posts: 307
Nickname: miata71
Registered: Mar, 2006

Re: When GC is Not Enough: Resource Management in Java Posted: Mar 29, 2006 2:14 PM
Reply to this message Reply
I have a utility class called Finally with a ton of close methods which check for null args and eat Exceptions, e.g.

public static void close(OutputStream stream) {
if (stream != null) {
try {
stream.close();
}
catch (IOException e) {
// ignore
}
}
}

Roland Pibinger

Posts: 93
Nickname: rp123
Registered: Jan, 2006

Re: When GC is Not Enough: Resource Management in Java Posted: Mar 29, 2006 2:25 PM
Reply to this message Reply
>    public static void close(OutputStream stream) {
>       if (stream != null) {
>          try {
>             stream.close();
>          }
>          catch (IOException e) {
>             // ignore         
>          }
>       }
>    } 

You really ignore an exception thrown from stream.close()???

Morgan Conrad

Posts: 307
Nickname: miata71
Registered: Mar, 2006

Exceptions in Stream.close() Posted: Mar 29, 2006 6:05 PM
Reply to this message Reply
If there was an IOException in the preceeding "real" code, say, a file is locked or an invalid path or a faulty format, the IOException still gets thrown. So I'm only ignoring Exceptions if the first part succeeds.

If, within the finally clause, the already opened file and modified or read file cannot be closed, I have no idea how this exception can occur other than a catastrophe (should I put up a dialog "hey, your disk just crashed"?), in which case I don't think handling the Exception will be worth the effort.

Bill Venners

Posts: 2248
Nickname: bv
Registered: Jan, 2002

Re: Exceptions in Stream.close() Posted: Mar 29, 2006 9:15 PM
Reply to this message Reply
> If there was an IOException in the preceeding "real" code,
> say, a file is locked or an invalid path or a faulty
> format, the IOException still gets thrown. So I'm only
> ignoring Exceptions if the first part succeeds.
>
> If, within the finally clause, the already opened file and
> modified or read file cannot be closed, I have no idea how
> this exception can occur other than a catastrophe (should
> I put up a dialog "hey, your disk just crashed"?), in
> which case I don't think handling the Exception will be
> worth the effort.

I can't think of a scenario either off the top of my head, but close() on an output stream will also cause data to be flushed if it hasn't already been. So if close() on an output stream fails, it might mean the outputting you were doing didn't actually complete, and I think that may very well be something you want to handle if you want your system to be "solid."

http://www.artima.com/intv/solid.html

Morgan Conrad

Posts: 307
Nickname: miata71
Registered: Mar, 2006

Re: Exceptions in Stream.close() Posted: Mar 30, 2006 1:41 PM
Reply to this message Reply
Good point on close() also doing flush(). I was going to comment that you are safe if you do the flush() up in the try{} part, so that Exceptions in flush get thrown. But I just re-read the javadocs on OutputStream.flush(). Which are real scary...

OutputStream.flush() does nothing! And the general contract on flush() makes no guarantee that the data made it to disk.

Since you can never tell if it "really" worked, one could argue that you may as well ignore Exceptions. But I don't write mission critical apps...

Flat View: This topic has 10 replies on 1 page
Topic: When GC is Not Enough: Resource Management in Java Previous Topic   Next Topic Topic: Applying Aspects in Practice


Sponsored Links



Google
  Web Artima.com   

Copyright © 1996-2014 Artima, Inc. All Rights Reserved. - Privacy Policy - Terms of Use - Advertise with Us