You are viewing an old version of this page. View the current version.

Compare with Current View Page History

« Previous Version 48 Next »

Serialization can extend the lifetime of objects, consequently preventing garbage collection of those objects. The ObjectOutputStream ensures that each object is written to the stream only once by retaining a reference (or handle) to each object written to the stream. When a previously-written object is subsequently written to the stream again, it is replaced with a reference to the originally-written data in the stream. Note that this substitution takes place without regard to whether the object's contents have changed in the interim. This table of references prevents garbage collection of the previously-written objects because the garbage collector cannot collect live references.

This behavior is both desirable and correct for data that potentially contains arbitrary object graphs, especially when the graphs are fully allocated and constructed prior to serialization. However, it can lead to memory exhaustion when serializing data that both lacks references to other objects being serialized and also can be allocated in part or in full after serialization has begun. One example of such data is serializing a data stream from an external sensor. In such cases, programs must take additional action to avoid memory exhaustion. That is, programs reading in independent serialized data must reset the object cache between reads to prevent memory exhaustion.

This rule is a specific instance of the more general rule MSC07-J. Do not assume infinite heap space.

Noncompliant Code Example

This noncompliant code example reads and serializes data from an external sensor. Each invocation of the readSensorData() method returns a newly created SensorData instance, containing a megabyte of data. SensorData instances contain data and arrays, but lack any references to other SensorData objects; this is a pure data stream.

The ObjectOutputStream maintains a cache of previously-written objects, as discussed above; consequently, all SensorData objects remain alive until the cache itself becomes garbage. This can result in an OutOfMemoryError, because the stream remains open while new objects are being written to it.

class SensorData implements Serializable {
  // 1MB of data per instance!
  ... 
  public static SensorData readSensorData() {...}
  public static boolean isAvailable() {...}
}

class SerializeSensorData {
  public static void main(String[] args) throws IOException {
    ObjectOutputStream out = null;
    try {
      out = new ObjectOutputStream(
          new BufferedOutputStream(new FileOutputStream("ser.dat")));
      while (SensorData.isAvailable()) {
        // note that each SensorData object is 1MB in size
        SensorData sd = SensorData.readSensorData();
        out.writeObject(sd);
      }
    } finally {
      out.close();
    }
  }
}

Compliant Solution

This compliant solution takes advantage of the known properties of the sensor data by resetting the output stream after each write. The reset clears the output stream's internal object cache; consequently, the cache no longer maintains references to previously-written SensorData objects. The garbage collector is able to collect SensorData instances that are no longer needed.

class SerializeSensorData {
  public static void main(String[] args) throws IOException {
    ObjectOutputStream out = null;
    try {
      out = new ObjectOutputStream(
          new BufferedOutputStream(new FileOutputStream("ser.dat")));
      while (SensorData.isAvailable()) {
        // note that each SensorData object is 1MB in size
        SensorData sd = SensorData.readSensorData();
        out.writeObject(sd);
        out.reset(); // reset the stream
      }
    } finally {
      out.close();
    }
  }
}

Risk Assessment

Memory and resource leaks during serialization can consume all available memory or crash the JVM.

Rule

Severity

Likelihood

Remediation Cost

Priority

Level

SER10-J

low

unlikely

low

P3

L3

Related Guidelines

MITRE CWE

CWE ID 400, "Uncontrolled Resource Consumption (aka 'Resource Exhaustion')"

 

CWE ID 770, "Allocation of Resources Without Limits or Throttling"

Bibliography

<ac:structured-macro ac:name="unmigrated-wiki-markup" ac:schema-version="1" ac:macro-id="d7f13870-d821-4f25-9266-089c2a772133"><ac:plain-text-body><![CDATA[

[[API 2006

AA. Bibliography#API 06]]

 

]]></ac:plain-text-body></ac:structured-macro>

<ac:structured-macro ac:name="unmigrated-wiki-markup" ac:schema-version="1" ac:macro-id="88c8923e-efe8-425a-a097-0ea83524190d"><ac:plain-text-body><![CDATA[

[[Harold 2006

AA. Bibliography#Harold 06]]

13.4. Performance

]]></ac:plain-text-body></ac:structured-macro>

<ac:structured-macro ac:name="unmigrated-wiki-markup" ac:schema-version="1" ac:macro-id="49754ec6-2c5b-49c0-ad87-b7c9fc4d13a2"><ac:plain-text-body><![CDATA[

[[Sun 2006

AA. Bibliography#Sun 06]]

"Serialization specification"

]]></ac:plain-text-body></ac:structured-macro>


SER09-J. Do not invoke overridable methods from the readObject method      13. Serialization (SER)      

  • No labels