From: <rb...@us...> - 2014-03-12 19:46:50
|
Revision: 9175 http://sourceforge.net/p/htmlunit/code/9175 Author: rbri Date: 2014-03-12 19:46:46 +0000 (Wed, 12 Mar 2014) Log Message: ----------- more GC friendly finalizer for the ImageInputStream Modified Paths: -------------- trunk/htmlunit/src/changes/changes.xml trunk/htmlunit/src/main/java/com/gargoylesoftware/htmlunit/html/HtmlImage.java Modified: trunk/htmlunit/src/changes/changes.xml =================================================================== --- trunk/htmlunit/src/changes/changes.xml 2014-03-11 22:19:27 UTC (rev 9174) +++ trunk/htmlunit/src/changes/changes.xml 2014-03-12 19:46:46 UTC (rev 9175) @@ -8,6 +8,10 @@ <body> <release version="2.15" date="???" description="Bugfixes"> + <action type="fix" dev="rbri" issue="1583" due-to="Carsten"> + Optimized image data handling; the finalizer needed for the ImageInputStream + no longer stops the image node itself from being gc'ed. + </action> <action type="update" dev="rbri"> INCOMPATIBLE CHANGE: All public methods of CookieManager are taking care of the isCookiesEnabled() state. Subclasses have to do the same. This was done as part of the migration to the new HttpClient Modified: trunk/htmlunit/src/main/java/com/gargoylesoftware/htmlunit/html/HtmlImage.java =================================================================== --- trunk/htmlunit/src/main/java/com/gargoylesoftware/htmlunit/html/HtmlImage.java 2014-03-11 22:19:27 UTC (rev 9174) +++ trunk/htmlunit/src/main/java/com/gargoylesoftware/htmlunit/html/HtmlImage.java 2014-03-12 19:46:46 UTC (rev 9175) @@ -61,7 +61,7 @@ private int lastClickX_; private int lastClickY_; private WebResponse imageWebResponse_; - private ImageReader imageReader_; + private ImageData imageData_; private boolean downloaded_; private boolean onloadInvoked_; @@ -332,8 +332,7 @@ * @throws IOException if an error occurs while downloading or reading the image */ public int getHeight() throws IOException { - readImageIfNeeded(); - return imageReader_.getHeight(0); + return getImageReader().getHeight(0); } /** @@ -345,8 +344,7 @@ * @throws IOException if an error occurs while downloading or reading the image */ public int getWidth() throws IOException { - readImageIfNeeded(); - return imageReader_.getWidth(0); + return getImageReader().getWidth(0); } /** @@ -359,7 +357,7 @@ */ public ImageReader getImageReader() throws IOException { readImageIfNeeded(); - return imageReader_; + return imageData_.getImageReader(); } /** @@ -397,22 +395,23 @@ final WebRequest request = new WebRequest(url, accept); request.setAdditionalHeader("Referer", page.getUrl().toExternalForm()); imageWebResponse_ = webclient.loadWebResponse(request); - imageReader_ = null; + imageData_ = null; downloaded_ = true; } } private void readImageIfNeeded() throws IOException { downloadImageIfNeeded(); - if (imageReader_ == null) { + if (imageData_ == null) { final ImageInputStream iis = ImageIO.createImageInputStream(imageWebResponse_.getContentAsStream()); final Iterator<ImageReader> iter = ImageIO.getImageReaders(iis); if (!iter.hasNext()) { iis.close(); throw new IOException("No image detected in response"); } - imageReader_ = iter.next(); - imageReader_.setInput(iis); + final ImageReader imageReader = iter.next(); + imageReader.setInput(iis); + imageData_ = new ImageData(imageReader); } } @@ -493,26 +492,6 @@ } /** - * {@inheritDoc} - */ - @Override - protected void finalize() { - if (imageReader_ != null) { - try { - final ImageInputStream stream = (ImageInputStream) imageReader_.getInput(); - if (stream != null) { - stream.close(); - } - imageReader_.setInput(null); - imageReader_.dispose(); - } - catch (final IOException e) { - LOG.error(e.getMessage() , e); - } - } - } - - /** * <span style="color:red">INTERNAL API - SUBJECT TO CHANGE AT ANY TIME - USE AT YOUR OWN RISK.</span><br/> * * Returns the default display style. @@ -523,4 +502,46 @@ public DisplayStyle getDefaultStyleDisplay() { return DisplayStyle.INLINE; } + + /** + * Wraps the ImageReader for an HtmlImage. This is necessary because an object with a finalize() + * method is only garbage collected after the method has been run. Which causes all referenced + * objects to also not be garbage collected until this happens. Because a HtmlImage references a lot + * of objects which could all be garbage collected without impacting the ImageReader it is better to + * wrap it in another class. + */ + private static final class ImageData { + + private final ImageReader imageReader_; + + public ImageData(final ImageReader imageReader) { + imageReader_ = imageReader; + } + + public ImageReader getImageReader() { + return imageReader_; + } + + /** + * {@inheritDoc} + */ + @Override + protected void finalize() { + if (imageReader_ != null) { + try { + final ImageInputStream stream = (ImageInputStream) imageReader_.getInput(); + if (stream != null) { + stream.close(); + } + } + catch (final IOException e) { + LOG.error(e.getMessage(), e); + } + finally { + imageReader_.setInput(null); + imageReader_.dispose(); + } + } + } + } } |