2

I am using try-with-resources, introduced in Java 7. This program downloads several HTTP resources and compresses them.

import java.io.*;
import java.net.*;
import java.util.zip.GZIPOutputStream;

class Main {
    private static byte[] downloadAndCompress(URL url) throws IOException {
        ByteArrayOutputStream byteOutput = new ByteArrayOutputStream();
        HttpURLConnection connection = (HttpURLConnection)url.openConnection();
        try(
            Closeable closeConnection = () -> connection.disconnect();
            OutputStream gzipOutput = new GZIPOutputStream(byteOutput);
        ) {
            connection.connect();
            byte[] buffer = new byte[1024];
            int bytesRead;
            while (0 <= (bytesRead = connection.getInputStream().read(buffer))) {
                gzipOutput.write(buffer, 0, bytesRead);
            }
        }
        return byteOutput.toByteArray();

    }

    public static void main(String[] args) throws Exception {
        for (int i = 0; i < 10; i++) {
            URL url = new URL("http://example.com?" + i);
            try {
                downloadAndCompress(url);
                // TODO: do something with result
            } catch (IOException e) {
                System.err.println("Failed to download " + url + ": " + e);
            }
        }
    }
}

I wondered what would happen if (1) the request failed and (2) writing the gzip trailer exceeded available heap memory.

So I rewrote downloadAndCompare to test:

        HttpURLConnection connection = (HttpURLConnection)url.openConnection();
        try(
            Closeable closeConnection = () -> connection.disconnect();
            OutputStream gzipOutput = new GZIPOutputStream(byteOutput) {
                public void close() { throw new OutOfMemoryError(); }
            };
        ) {
            throw new IOException("failed to download");
        }

Running it yields

Failed to download http://example.com?0: java.io.IOException
Failed to download http://example.com?1: java.io.IOException
Failed to download http://example.com?2: java.io.IOException
Failed to download http://example.com?3: java.io.IOException
Failed to download http://example.com?4: java.io.IOException
Failed to download http://example.com?5: java.io.IOException
Failed to download http://example.com?6: java.io.IOException
Failed to download http://example.com?7: java.io.IOException
Failed to download http://example.com?8: java.io.IOException
Failed to download http://example.com?9: java.io.IOException

It appears that try-with-resources suppresses all Throwables encountered in closing resources, be they OutOfMemoryError, InterruptedException, or StackOverflowError.

I read that it is a very bad thing to continue after OOM and other errors happen.

How does that reconcile with this language construct? Is ignoring OOM fine? Is try-with-resource safe to use?

Paul Draper
  • 78,542
  • 46
  • 206
  • 285
  • 6
    `try-with-resource` itself only guarantee's that `close` will be called on those resources when the `try` block exists, regardless of how it exited (error, normal flow, what ever) - it's then up to the `catch` block to make determinations about what to do if a particular exception is triggered. So instead of worrying about the `try-with-resource` block, focus your efforts on what you are doing in the `catch` block when one or more exceptions occur – MadProgrammer Oct 12 '17 at 23:34
  • try-with-resources only suppresses exceptions from resource closing if an exception was raised inside the try, and you have to suppress one or the other. If it instead suppressed the exception from the try, you'd be complaining that an OutOfMemoryError in the try was being suppressed. – user2357112 Oct 12 '17 at 23:36
  • 1
    Short answer: gotta suppress *something*. – user2357112 Oct 12 '17 at 23:38
  • Use `Throwable.getSuppressed()` if you're interested in handling those errors. – jontro Oct 13 '17 at 00:07
  • In this example, the method would terminate with an exception and the incomplete gzip would be discarded before it was visible to the rest of the program. In this case, it didn't matter that the OOME was suppressed. Do you have another example where it could matter? – erickson Dec 24 '17 at 20:13

0 Answers0