scalastreamgarbage-collectionprintwriterzipoutputstream

When i close the zipoutputstream does the writer objects associated with it will also be closed and cleaned by GC


Trying to create a ZIP file having 3 files inside it. I am using ZipOutputStream for creating ZIP file and Printwriter to create and write files into Zip.

When i close printwriter object after writing a file closes the stream as well, which i know. But if i close zip output stream after writing into zip will it close and clean my printwriter objects as well

  def main(args: Array[String]): Unit = {
    val file: File = new File("Hello.zip")
    val zos: ZipOutputStream = new ZipOutputStream(new FileOutputStream(file))
    val fileNames = Array("Happy1.csv", "Happy2.csv", "Happy3.csv", "Happy4.csv")
    fileNames.foreach { tempfile =>
      zos.putNextEntry(new ZipEntry(tempfile))
      Try {
        val writer = new PrintWriter(new OutputStreamWriter(zos))
        writer.println("Hello,World")
        writer.flush()
        // writer.close() // Closing writer closes the stream so commented it
      }
    }
    zos.closeEntry()
    zos.flush()
    zos.close() // will my writer object also closed and cleaned by gc
  }

Solution

  • Closing the ZipOutputStream will not close the PrintWriter nor OutputStreamWriter; in fact, it doesn’t even know that these writers exist. The references point the other way, PrintWriter has a reference to the OutputStreamWriter to which it will delegate writes, flush and close operations, just as the OutputStreamWriter will delegate to the ZipOutputStream.

    But the PrintWriter and the OutputStreamWriter do not bear any system resources, except for those encapsulated by the target output stream, i.e. the ZipOutputStream. When you called flush() to enforce writing any pending data, you did already everything needed for cleanup, as you close the ZipOutputStream at the end.

    Normally, you use the decorating stream or writer to control the lifetime, sometimes not even keeping a reference to the encapsulated stream or writer. So closing the decorating stream or writer is mandatory then.

    But here, where the delegation of the close operation is not intended, you have to care to close the underlying stream. This is special, but not uncommon. This always happens when you have a file format or protocol with embedded sub formats. The ZipOutputStream class even has a finish method dedicated to the scenario that a zip file is embedded in another stream. PrintWriter doesn’t have such method, on the other hand, for a PrintWriter, calling flush() is already sufficient for finishing the operation.

    Note that you should use Scala’s equivalent of try-with-resources to close the ZipOutputStream, if there is one. Further note that you could use the same PrintWriter throughout the entire operation, if you keep caring to flush it at the end of each entry. That would allow to close it at the end like in simple wrapped stream scenarios.