Java Theory and Practice: Good Housekeeping practices

Source: Internet
Author: User
Tags final finally block garbage collection resource return socket thread client
Garbage collection is almost every developer's favorite Java platform feature that simplifies development and eliminates all kinds of potential code errors. But while garbage collection can generally leave you with no resources to manage, you sometimes have to do some housekeeping on your own. In this article, Brian Goetz discusses the limitations of garbage collection and points out the situations where you have to do your own in-house processing.

As a child, parents always told us to play the toys after the good. If you think about it, in fact, this nagging is not excessive, to keep neat because there are actual restrictions, the room does not have much space, if the pile is full of toys, so even walk have nowhere to go.

If you have enough space, it is not necessary to keep tidy. The more space you have, the less you need to keep it neat. The famous Ballad of Arlo Guthrie Alice's Restaurant massacre illustrates this point:

They lived in the hall downstairs, the chairs were all moved away, and there was an empty room, so they thought, for a long time, they didn't have to throw the rubbish out, there were some places to put rubbish ...

In any case, garbage collection can help us reduce housekeeping work.

   to explicitly release resources

Most of the resources used in Java programs are objects, and garbage collection does a good job of cleaning up objects. Therefore, you can use any number of strings. The garbage collector ultimately calculates when they will fail without your intervention and reclaim the memory they use.

On the other hand, non-memory resources such as file handles and socket handles must be explicitly released by programs, such as the use of close (), Destroy (), shutdown (), or release (). Some classes, such as file handle flow implementations in platform class libraries, provide finalizers (finalizer) as security assurances that finalizers can do this release work when the garbage collector determines that the program is no longer using resources and the program forgets to release resources. However, although the file handle provides a finalizer to release resources for you when you forget, it is best to explicitly release resources after use. This can release resources earlier and reduce the likelihood of resource depletion.

For some resources, it is not advisable to wait until the end (finalization) to release them. For important resources, such as lock acquisition and semaphore licenses, lock or semaphore may not be garbage collected until very late. For a resource such as a database connection, if you wait for the end, you will definitely run out of resources. Many database servers accept only a certain number of connections, depending on the allowable capacity. If the server application opens a new database connection for each request and then does not use it, then the database will reach its maximum capacity by far from the end of the connection that the Terminator is no longer required to close.

   Resources Limited to one method

Most resources do not sustain the lifecycle of the entire application, instead they are used only for an active lifecycle. When an application opens a file handle to read a file to process a document, it typically reads the file and no longer requires a file handle.

In the simplest case, the resource is fetched, used, and released in the same method invocation, such as the loadpropertiesbadly () method in Listing 1:

Listing 1. Improper acquisition, use, and release of resources in a method--don't do this

public static Properties loadpropertiesbadly (String fileName)
Throws IOException {
FileInputStream stream = new FileInputStream (fileName);
Properties Props = new properties ();
Props.load (stream);
Stream.Close ();
return props;
}
Unfortunately, there is a potential resource leak in this example. If all goes well, the stream will be closed before the method returns. However, if the Props.load () method throws a IOException, the stream is not closed (until the garbage collector runs its finalizer). The solution is to use the try...finally mechanism to ensure that the stream is closed, regardless of whether an error occurred, as shown in Listing 2:

Listing 2. Getting, using, and releasing resources correctly in a method

public static Properties loadproperties (String fileName)
Throws IOException {
FileInputStream stream = new FileInputStream (fileName);
try {
Properties Props = new properties ();
Props.load (stream);
return props;
}
finally {
Stream.Close ();
}
}
Note that resource fetching (open file) is done outside a try block, and if you put it in a try block, the finally block runs even if the resource fetch throws an exception. Not only is this method inappropriate (you cannot release resources that you do not have), the code in finally blocks can also throw its own exceptions, such as NullPointerException. The exception thrown from the finally block replaces the exception that caused the block to exit, which means that the original exception was lost and cannot be used to help with debugging. It's not always as easy as it looks.

It is reliable to use finally to release the resources acquired in the method, but it can easily become difficult to handle when multiple resources are involved. Consider a method that uses a JDBC Connection to execute queries and iteration ResultSet. The method obtains a Connection, uses it to create a Statement, and executes Statement to get a ResultSet. However, the intermediate JDBC objects Statement and ResultSet have their own close () methods, and when you are done with them, you should release the intermediate objects. However, the "obvious" approach to resource release does not work, as shown in Listing 3:

Listing 3. Unsuccessful attempts to free multiple resources--Don't do it

public void Enumeratefoo () throws SQLException {
Statement Statement = null;
ResultSet ResultSet = null;
Connection Connection = getconnection ();
try {
statement = Connection.createstatement ();
ResultSet = Statement.executequery ("SELECT * from Foo");
Use ResultSet
}
finally {
if (ResultSet!= null)
Resultset.close ();
if (statement!= null)
Statement.close ();
Connection.close ();
}
}
The reason this "solution" is unsuccessful is that the close () method of the ResultSet and Statement can throw the SQLException itself, which causes the close () statement in the later finally block to execute. You have several options here, and each one is annoying: use a try ... The catch block encapsulates each close (), nesting try...finally blocks like Listing 4, or writing a small framework to manage resource acquisition and release.

Listing 4. A reliable way to release multiple resources

public void Enumeratebar () throws SQLException {
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 ();
}
}
}
}

Private Connection getconnection () {
return null;
}
Almost everything can throw an exception.

We all know that we should use finally to release heavyweight objects like database connections, but we're not always so careful, we can remember to use it to close the stream (after all, the Terminator will do this for us, right?). )。 It is easy to forget to use finally when the code that uses the resource does not throw the checked exception. Listing 5 shows the implementation of the Add () method for the bound connection, which uses semaphore to enforce the binding and effectively allows the client to wait for space to be available:

Listing 5. Fragile implementations of bound connections--don't do this

public class Leakyboundedset<t> {
Private final set<t> Set = ...
Private final semaphore Sem;

public leakyboundedset (int bound) {
SEM = new semaphore (bound);
}

Public boolean Add (T O) throws Interruptedexception {
Sem.acquire ();
Boolean wasadded = Set.add (o);
if (!wasadded)
Sem.release ();
return wasadded;
}
}
Leakyboundedset first waits for a license to become available (indicating that there is room in the connection), and then attempts to add the element to the connection. Add operation if the element fails because it is already in the connection, it releases the license (because it does not actually use the space it retains).

Leakyboundedset-related issues do not need to jump out immediately: what if Set.add () throws an exception? If there is a flaw in the Set implementation, or if the Equals () or hashcode () implementation (in SortedSet case is compareTo () implementation) is defective because the element is already in set when the element is added. Of course, the solution is to use finally to release the semaphore license, which is a simple but easily forgotten method. These types of errors are rarely exposed during the test and thus become a time bomb that can explode at any moment. Listing 6 shows a more reliable implementation of Boundedset:

Listing 6. Use a semaphore to securely bind Set

public class Boundedset<t> {
Private final set<t> Set = ...
Private final semaphore Sem;

public boundedhashset (int bound) {
SEM = new semaphore (bound);
}

Public boolean Add (T O) throws Interruptedexception {
Sem.acquire ();
Boolean wasadded = false;
try {
wasadded = Set.add (o);
return wasadded;
}
finally {
if (!wasadded)
Sem.release ();
}
}
}


Code review tools like FindBugs can detect instances of improper resource releases, such as opening a stream in a method without closing it.

   resources with an arbitrary life cycle

For resources with arbitrary lifecycles, we want to go back to the time of the C language, which is to manually manage the resource lifecycle. In a server application, a persistent network connection from a client to a server exists during a session (such as a multiplayer game server), and each user's resources (including socket connections) must be freed when the user exits. Good organization is helpful; if the role references for each user resource are kept in a Activeuser object, they can be freed when the Activeuser is released, whether it is released explicitly or through garbage collection.

Resources with arbitrary lifecycles are almost always stored in a global collection (or from here). To avoid resource leaks, it is important to identify when resources are no longer needed and can be removed from this global collection. (A previous article "using weak references to block memory leaks" gives some useful tips.) At this point, any non-memory resources associated with the resource can be freed at the same time because you know that the resource will be freed.

   Resource Ownership

One of the key techniques to ensure timely resource release is to maintain a strict hierarchy of ownership, where ownership has the responsibility to release resources. If the application creates a thread pool, and the threads pool creates the threading, the thread is the resource that must be freed before the program can quit. But the application does not own the thread, but rather the thread pool owns the thread, so the thread pool must be responsible for freeing the threads. Of course, the thread pool can release threads until it itself is freed by the application.

Maintaining a hierarchy of ownership helps not to lose control, where each resource has the resources it obtains and is responsible for releasing them. The result of this rule is that each resource that cannot be collected separately by garbage collection (that is, a resource that directly or indirectly owns a resource that cannot be freed from garbage collection) must provide some lifecycle support, such as the Close () method.

   Terminator

If the platform library provides finalizers to clear open file handles, which greatly reduces the risk of forgetting to explicitly close these handles, why not use finalizers more? For a number of reasons, the most important one is that finalizers are difficult to write correctly (and are easily written incorrectly). Finalizers are not only difficult to write correctly, and the timing of finalization is uncertain, and it is not guaranteed that finalizers will eventually run. And the end also brings overhead to instantiating and garbage collection of the objects that can be terminated. Do not rely on finalizers as the primary way to release resources.

   Concluding remarks

Garbage collection has done a lot of terrible cleanup work for us, but some resources still need explicit release, such as file handles, socket handles, threads, database connections, and semaphore licenses. When the lifecycle of a resource is bound to the lifecycle of a particular call frame, we can usually use the finally block to release the resource, but long-lived resources require a strategy to ensure that they are eventually freed. For any such object, which directly or indirectly has an object that needs to be explicitly disposed, you must provide a lifecycle method-such as Close (), release (), Destroy (), etc.-to ensure reliable cleanup.



Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.