Oracle8i Java Developer's Guide Release 3 (8.1.7) Part Number A83728-01 |
Library | Service | Contents | Index

## Managing Your Operating System Resources

Operating system resources are a limited commodity on any computer. Because Java is targeted at providing a computing platform as well as a programming language, it contains platform-independent classes and frameworks for accessing platform-specific resources. The Java class methods access operating system resources through the JVM. Java has potential problems with this model, because programmers rely on the garbage collector to manage all resources, when all that the garbage collector manages is Java objects, not the operating system resources that the Java object holds on to.

In addition, because the Aurora JVM is embedded in the database, your operating system resources, which are contained within Java objects, can be invalidated if they are maintained across calls within a session.

The following sections discusses these potential problems:

- Overview of Operating System Resources

- Garbage Collection and Operating System Resources

- Operating System Resources Affected Across Calls

### Overview of Operating System Resources

In general, your operating system resources contain the following:

memory | Aurora manages memory internally, allocating memory as you create new objects and freeing objects as you no longer need them. The language and class libraries do not support a direct means to allocate and free memory. "Automated Storage Management" discusses garbage collection.
files | Java contains classes that represent file resources. Instances of these classes hold on to your operating system's file constructs, such as file handles, which can become invalid between calls in a session.
sockets | Java contains classes that represent socket resources. Instances of these classes hold on to socket constructs, some of which can become invalid between calls in a session. See "Sockets" for information specific to maintaining sockets across calls.
threads | Threads are discouraged within the Aurora JVM because of scalability issues. However, you can have a multi-threaded application within the database. "Threading in JServer" discusses in detail the Aurora's JVM threading model.

#### Operating System Resource Access

By default, a Java user does not have direct access to most operating system resources. A system administrator may give permission to a user to access these resources by modifying the JVM security restrictions. The JVM security enforced upon system resources conforms to Java 2 security. See "Java 2 Security" for more information.

#### Operating System Resource Lifetime

You access operating system resources using the standard core Java classes and methods. Once you access a resource, the time that it remains active (usable) varies according to the type of resource.

Resource | Lifetime
--- | ---
Files | The system closes all files left open when a database call ends.
Memory | Memory is garbage collected as described in "Automated Storage Management".
Threads | All threads are terminated when a call ends.
Objects that depend on operating system resources | Regardless of the usable lifetime of the object (for example, the defined lifetime for a thread object), the Java object can be valid for the duration of the session. This can occur, for example, if the Java object is stored in a static class variable or a class variable references it directly or indirectly. If you attempt to use one of these Java objects after its usable lifetime is over, Aurora throws an exception. This is true for the following examples: If an attempt is made to read from a `java.io.FileInputStream` that was closed at the end of a previous call, a `java.io.IOException` is thrown. `java.lang.Thread.isAlive()` is false for any Thread object running in a previous call and still accessible in a subsequent call.
Sockets | Sockets can exist across calls. ServerSockets on an MTS server terminate when the call ends. ServerSockets on a dedicated server can exist across calls. See "Sockets" more information.

### Garbage Collection and Operating System Resources

Imagine that memory is divided up into two realms: Java object memory and operating system constructs. The Java object memory realm contains all objects and variables. Operating system constructs include resources that the operating system allocates to the object when it asks. These resources include files, sockets, and so on.

Basic programming rules dictate that you close all memory--both Java objects and operating system constructs. Java programmers incorrectly assume that all memory is freed by the garbage collector. The garbage collector was created to collect all unused Java object memory. However, it does not close any operating system constructs. All operating system constructs must be closed by the program before the Java object is collected.

For example, whenever an object opens a file, the operating system creates the file and gives the object a file handle. If the file is not closed, the operating system will hold the file handle construct open until the call ends or JVM exits. This can cause you to run out of these constructs earlier than necessary. There are a finite number of handles within each operating system. To guarantee that you do not run out of handles, close your resources before exiting the method. This includes closing the streams attached to your sockets. You should close the streams attached to the socket before closing the socket.

So why not expand the garbage collector to close all operating system constructs? For performance reasons, the garbage collector cannot examine each object to see if it contains a handle. Thus, the garbage collector collects Java objects and variables, but does not issue the appropriate operating system methods for freeing any handles.

Example 2-6 shows how you should close the operating system constructs.

#### Example 2-6 Closing your operating system resources

    public static void addFile(String[] newFile) {
     File inFile = new File(newFile);
     FileReader in = new FileReader(inFile);
     int i;

     while ((i = in.read()) != -1)
        out.write(i);
     /*closing the file, which frees up the operating system file handle*/
     in.close();
     }

If you do not close the `in` file, eventually the `File` object will be garbage collected. However, even if the `File` object is garbage collected, the operating system still believes that the file is in use, because it was not closed.

Note: You might want to use Java finalizers to close resources. However, finalizers are not guaranteed to run in a timely manner. Instead, finalizers are put on a queue to execute when the garbage collector has time. If you close your resources within your finalizer, it might not be freed up until the JVM exits. The best approach is to close your resources within the method.

### Operating System Resources Affected Across Calls

You should close resources that are local to a single call when the call ends. However, for static objects that hold on to operating system resources, you must be aware of how these resources are affected after the call ends.

The JVM automatically closes any open operating system constructs--in Example 2-7, the file handle--when the call ends. This can affect any operating system resources within your Java object. For example, if you have a file opened within a static variable, the file handle is closed at the end of the call for you. So, if you hold on to the `File` object across calls, the next usage of the file handle throws an exception.

In Example 2-7, class `Concat` enables multiple files to be written into a single file, `outFile`. On the first call, `outFile` is created. The first input file is opened, read, input into `outFile`, and the call ends. Because `outFile` is statically defined, it is moved into session space between call invocations. However, the file handle--that is, the `FileDescriptor`--is closed at the end of the call. The next time you call `addFile`, you will get an exception.

#### Example 2-7 Compromising your operating system resources

    public class Concat {
      static File outFile = new File("outme.txt");
      FileWriter out = new FileWriter(outFile);

    public static void addFile(String[] newFile) {
     File inFile = new File(newFile);
     FileReader in = new FileReader(inFile);
     int i;

     while ((i = in.read()) != -1)
        out.write(i);
     in.close();
     }
    }

There is a workaround. To make sure that your handles stay valid, you should close your files, buffers, and so on, at the end of every call; reopen the resource at the beginning of the next call. Another option is to use the database, rather than using operating system resources. For example, try to use database tables, rather than a file. Or simply do not store operating system resources within static objects expected to live across calls; use operating system resources only within objects local to the call.

Example 2-8 shows how you can perform concatenation, as in Example 2-7, without compromising your operating system resources. The `addFile` method opens the `outme.txt` file within each call, making sure that anything written into the file is appended to the end. At the end of each call, the file is closed. Two things occur:

1. The `File` object no longer exists outside of a call.

2. The operating system resource, the `outme.txt` file, is reopened for each call. If you had made the `File` object a static variable, the closing of `outme.txt` within each call would ensure that the operating system resource is not compromised.

#### Example 2-8 Correctly managing your operating system resources

    public class Concat {

    public static void addFile(String[] newFile) {
     /*open the output file each call; make sure the input*/
     /*file is written out to the end by making it "append=true"*/
     FileWriter out = new FileWriter("outme.txt", TRUE);
     File inFile = new File(newFile);
     FileReader in = new FileReader(inFile);
     int i;

     while ((i = in.read()) != -1)
        out.write(i);
     in.close();
     /*close the output file between calls*/
     out.close();
     }
    }

#### Sockets

Sockets are used in setting up a connection between a client and a server. For each database connection, sockets are used at either end of the connection. Your application does not set up the connection; the connection is set up by the underlying networking protocol: Net8's TTC or IIOP. See "Configuring JServer" for information on how to configure your connection.

You might also wish to set up another connection--for example, connecting to a specified URL from within one of the classes stored within the database. To do so, instantiate sockets for servicing the client and server sides of the connection.

- The `java.net.Socket()` constructor creates a client socket.

- The `java.net.ServerSocket()` constructor creates a server socket.

A socket exists at each end of the connection. The server-side of the connection that listens for incoming calls is serviced by a `ServerSocket`. The client-side of the connection that sends requests is serviced through a `Socket`. You can use sockets as defined within the JVM with the following restriction: a `ServerSocket` instance within an MTS server cannot exist across calls.

`Socket` | Because the client-side of the connection is outbound, the `Socket` instance can be serviced across calls within either an MTS or dedicated server.
`ServerSocket` | The server-side of the connection is a listener. Dedicated server--Your `ServerSocket` can listen across calls only within a dedicated server; the dedicated server exists solely for servicing the single client. MTS server--The `ServerSocket` is closed at the end of a call within an MTS server; the MTS uses shared servers, which move on to another client at the end of every call. You will receive an I/O exception stating that the socket was closed if you try to use the `ServerSocket` outside of the call it was created in.

| Copyright © 1996-2000, Oracle Corporation. All Rights Reserved. |
Library | Service | Contents | Index