-
Bug
-
Resolution: Unresolved
-
P4
-
8, 11, 17, 21
-
generic
-
generic
ADDITIONAL SYSTEM INFORMATION :
Any OS, reproducible with OpenJDK 64-Bit Server VM Zulu17.38+21-CA (build 17.0.5+8-LTS, mixed mode, sharing)
A DESCRIPTION OF THE PROBLEM :
NullPointerException happens at FileCacheImageOutputStream.java:229 when JVM is about to shut down.
We observed the same NPE right before JVM shutdown. While looking into this problem I found that com.sun.imageio.stream.StreamCloser could cause the issue. I think the sequence of events is as follows:
- application thread invokes ImageIO.write(RenderedImage im,
String formatName,
OutputStream output)
- internally it creates instance of FileCacheImageOutputStream if caching is available
- JVM has got a shutdown signal
- The shutdown hook registered by com.sun.imageio.stream.StreamCloser executes close actoins: com.sun.imageio.stream.StreamCloser.CloseAction#performAction. It closes all input streams still opened including those which are still in use by application threads
- Image is written by ImageIO.write method and "finally" section being executed where "close()" method of FileCacheImageOutputStream executed again. At this point internal field "cache" is already "null" because "close()" has been already executed once by "StreamCloser.CloseAction"
- NPE is thrown in the application thread
STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
Use ImageIO.write() method with default caching configuration. It should use FileCacheImageOutputStream, i.e. caching has to be allowed, tmp folder accessible
Invoke ImageIO.write() method with valid inputs try to generate a larger image so that method takes time to execute. In parallel request JVM shutdown
EXPECTED VERSUS ACTUAL BEHAVIOR :
EXPECTED -
No exceptions thrown in application thread(s)
ACTUAL -
NullPointerException sometimes happens at javax.imageio.stream.FileCacheImageOutputStream.close(FileCacheImageOutputStream.java:229)
---------- BEGIN SOURCE ----------
import javax.imageio.ImageIO;
import java.awt.image.BufferedImage;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
class Scratch {
public static void main(String[] args) throws InterruptedException {
Thread generator = new Thread(() -> {
int d = 10000;
BufferedImage image = new BufferedImage(d, d, BufferedImage.TYPE_INT_ARGB);
for (int x = 0; x < d; x++) {
for (int y = 0; y < d; y++) {
image.setRGB(x, y, x * y);
}
}
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
try {
System.out.println("Starting to write image..."); // terminate JVM as soon as you see this output
ImageIO.write(image, "png", outputStream);
Files.write(Path.of("out.png"), outputStream.toByteArray());
} catch (IOException e) {
e.printStackTrace();
throw new RuntimeException(e);
}
});
generator.start();
generator.join();
}
}
---------- END SOURCE ----------
CUSTOMER SUBMITTED WORKAROUND :
Disabling caching with a call to static method ImageIO.setUseCache(false); if amount of memory available to JVM allows to do so. With this setup com.sun.imageio.spi.OutputStreamImageOutputStreamSpi#createOutputStreamInstance will create an instance of MemoryCacheImageOutputStream which is not vulnerable to NPE in close() method
FREQUENCY : rarely
Any OS, reproducible with OpenJDK 64-Bit Server VM Zulu17.38+21-CA (build 17.0.5+8-LTS, mixed mode, sharing)
A DESCRIPTION OF THE PROBLEM :
NullPointerException happens at FileCacheImageOutputStream.java:229 when JVM is about to shut down.
We observed the same NPE right before JVM shutdown. While looking into this problem I found that com.sun.imageio.stream.StreamCloser could cause the issue. I think the sequence of events is as follows:
- application thread invokes ImageIO.write(RenderedImage im,
String formatName,
OutputStream output)
- internally it creates instance of FileCacheImageOutputStream if caching is available
- JVM has got a shutdown signal
- The shutdown hook registered by com.sun.imageio.stream.StreamCloser executes close actoins: com.sun.imageio.stream.StreamCloser.CloseAction#performAction. It closes all input streams still opened including those which are still in use by application threads
- Image is written by ImageIO.write method and "finally" section being executed where "close()" method of FileCacheImageOutputStream executed again. At this point internal field "cache" is already "null" because "close()" has been already executed once by "StreamCloser.CloseAction"
- NPE is thrown in the application thread
STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
Use ImageIO.write() method with default caching configuration. It should use FileCacheImageOutputStream, i.e. caching has to be allowed, tmp folder accessible
Invoke ImageIO.write() method with valid inputs try to generate a larger image so that method takes time to execute. In parallel request JVM shutdown
EXPECTED VERSUS ACTUAL BEHAVIOR :
EXPECTED -
No exceptions thrown in application thread(s)
ACTUAL -
NullPointerException sometimes happens at javax.imageio.stream.FileCacheImageOutputStream.close(FileCacheImageOutputStream.java:229)
---------- BEGIN SOURCE ----------
import javax.imageio.ImageIO;
import java.awt.image.BufferedImage;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
class Scratch {
public static void main(String[] args) throws InterruptedException {
Thread generator = new Thread(() -> {
int d = 10000;
BufferedImage image = new BufferedImage(d, d, BufferedImage.TYPE_INT_ARGB);
for (int x = 0; x < d; x++) {
for (int y = 0; y < d; y++) {
image.setRGB(x, y, x * y);
}
}
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
try {
System.out.println("Starting to write image..."); // terminate JVM as soon as you see this output
ImageIO.write(image, "png", outputStream);
Files.write(Path.of("out.png"), outputStream.toByteArray());
} catch (IOException e) {
e.printStackTrace();
throw new RuntimeException(e);
}
});
generator.start();
generator.join();
}
}
---------- END SOURCE ----------
CUSTOMER SUBMITTED WORKAROUND :
Disabling caching with a call to static method ImageIO.setUseCache(false); if amount of memory available to JVM allows to do so. With this setup com.sun.imageio.spi.OutputStreamImageOutputStreamSpi#createOutputStreamInstance will create an instance of MemoryCacheImageOutputStream which is not vulnerable to NPE in close() method
FREQUENCY : rarely
- relates to
-
JDK-8172965 NPE in javax.imageio.stream.FileCacheImageOutputStream.close()
- Open