Index: /trunk/src/org/openstreetmap/josm/gui/layer/geoimage/CorrelateGpxWithImages.java
===================================================================
--- /trunk/src/org/openstreetmap/josm/gui/layer/geoimage/CorrelateGpxWithImages.java	(revision 13219)
+++ /trunk/src/org/openstreetmap/josm/gui/layer/geoimage/CorrelateGpxWithImages.java	(revision 13220)
@@ -80,5 +80,4 @@
 import org.openstreetmap.josm.io.GpxReader;
 import org.openstreetmap.josm.spi.preferences.Config;
-import org.openstreetmap.josm.tools.ExifReader;
 import org.openstreetmap.josm.tools.GBC;
 import org.openstreetmap.josm.tools.ImageProvider;
@@ -459,6 +458,5 @@
             imgList.getSelectionModel().addListSelectionListener(evt -> {
                 int index = imgList.getSelectedIndex();
-                Integer orientation = ExifReader.readOrientation(yLayer.data.get(index).getFile());
-                imgDisp.setImage(yLayer.data.get(index).getFile(), orientation);
+                imgDisp.setImage(yLayer.data.get(index));
                 Date date = yLayer.data.get(index).getExifTime();
                 if (date != null) {
@@ -483,10 +481,9 @@
                 if (fc == null)
                     return;
-                File sel = fc.getSelectedFile();
-
-                Integer orientation = ExifReader.readOrientation(sel);
-                imgDisp.setImage(sel, orientation);
-
-                Date date = ExifReader.readTime(sel);
+                ImageEntry entry = new ImageEntry(fc.getSelectedFile());
+                entry.extractExif();
+                imgDisp.setImage(entry);
+
+                Date date = entry.getExifTime();
                 if (date != null) {
                     lbExifTime.setText(DateUtils.getDateTimeFormat(DateFormat.SHORT, DateFormat.MEDIUM).format(date));
Index: /trunk/src/org/openstreetmap/josm/gui/layer/geoimage/ImageDisplay.java
===================================================================
--- /trunk/src/org/openstreetmap/josm/gui/layer/geoimage/ImageDisplay.java	(revision 13219)
+++ /trunk/src/org/openstreetmap/josm/gui/layer/geoimage/ImageDisplay.java	(revision 13220)
@@ -46,5 +46,5 @@
 
     /** The file that is currently displayed */
-    private File file;
+    private ImageEntry entry;
 
     /** The image currently displayed */
@@ -215,12 +215,10 @@
     private class LoadImageRunnable implements Runnable, ImageObserver {
 
+        private final ImageEntry entry;
         private final File file;
-        private final int orientation;
-        private int width;
-        private int height;
-
-        LoadImageRunnable(File file, Integer orientation) {
-            this.file = file;
-            this.orientation = orientation == null ? -1 : orientation;
+
+        LoadImageRunnable(ImageEntry entry) {
+            this.entry = entry;
+            this.file = entry.getFile();
         }
 
@@ -229,12 +227,49 @@
             if (((infoflags & ImageObserver.WIDTH) == ImageObserver.WIDTH) &&
                 ((infoflags & ImageObserver.HEIGHT) == ImageObserver.HEIGHT)) {
-                this.width = width;
-                this.height = height;
-                synchronized (this) {
-                    this.notify();
+                synchronized (entry) {
+                    entry.setWidth(width);
+                    entry.setHeight(height);
+                    entry.notifyAll();
                     return false;
                 }
             }
             return true;
+        }
+
+        private boolean updateImageEntry(Image img) {
+            if (!(entry.getWidth() > 0 && entry.getHeight() > 0)) {
+                synchronized (entry) {
+                    img.getWidth(this);
+                    img.getHeight(this);
+
+                    long now = System.currentTimeMillis();
+                    while (!(entry.getWidth() > 0 && entry.getHeight() > 0)) {
+                        try {
+                            entry.wait(1000);
+                            if (this.entry != ImageDisplay.this.entry)
+                                return false;
+                            if (System.currentTimeMillis() - now > 10000)
+                                synchronized (ImageDisplay.this) {
+                                    errorLoading = true;
+                                    ImageDisplay.this.repaint();
+                                    return false;
+                                }
+                        } catch (InterruptedException e) {
+                            Logging.trace(e);
+                            Logging.warn("InterruptedException in {0} while getting properties of image {1}",
+                                    getClass().getSimpleName(), file.getPath());
+                            Thread.currentThread().interrupt();
+                        }
+                    }
+                }
+            }
+            return true;
+        }
+
+        private boolean mayFitMemory(long amountWanted) {
+            return amountWanted < (
+                   Runtime.getRuntime().maxMemory() -
+                   Runtime.getRuntime().totalMemory() +
+                   Runtime.getRuntime().freeMemory());
         }
 
@@ -242,28 +277,11 @@
         public void run() {
             Image img = Toolkit.getDefaultToolkit().createImage(file.getPath());
-
-            synchronized (this) {
-                width = -1;
-                img.getWidth(this);
-                img.getHeight(this);
-
-                while (width < 0) {
-                    try {
-                        this.wait();
-                        if (width < 0) {
-                            errorLoading = true;
-                            return;
-                        }
-                    } catch (InterruptedException e) {
-                        e.printStackTrace();
-                    }
-                }
-            }
-
-            long allocatedMem = Runtime.getRuntime().totalMemory() -
-                    Runtime.getRuntime().freeMemory();
-            long mem = Runtime.getRuntime().maxMemory()-allocatedMem;
-
-            if (mem > ((long) width*height*4)*2) {
+            if (!updateImageEntry(img))
+                return;
+
+            int width = entry.getWidth();
+            int height = entry.getHeight();
+
+            if (mayFitMemory(((long) width)*height*4*2)) {
                 Logging.info("Loading {0} using default toolkit", file.getPath());
                 tracker.addImage(img, 1);
@@ -271,5 +289,5 @@
                 // Wait for the end of loading
                 while (!tracker.checkID(1, true)) {
-                    if (this.file != ImageDisplay.this.file) {
+                    if (this.entry != ImageDisplay.this.entry) {
                         // The file has changed
                         tracker.removeImage(img);
@@ -280,12 +298,12 @@
                     } catch (InterruptedException e) {
                         Logging.trace(e);
-                        Logging.warn("InterruptedException in "+getClass().getSimpleName()+
-                                " while loading image "+file.getPath());
+                        Logging.warn("InterruptedException in {0} while loading image {1}",
+                                getClass().getSimpleName(), file.getPath());
                         Thread.currentThread().interrupt();
                     }
                 }
                 if (tracker.isErrorID(1)) {
+                    // the tracker catches OutOfMemory conditions
                     img = null;
-                    System.gc();
                 }
             } else {
@@ -293,11 +311,6 @@
             }
 
-            if (img == null || width <= 0 || height <= 0) {
-                tracker.removeImage(img);
-                img = null;
-            }
-
             synchronized (ImageDisplay.this) {
-                if (this.file != ImageDisplay.this.file) {
+                if (this.entry != ImageDisplay.this.entry) {
                     // The file has changed
                     tracker.removeImage(img);
@@ -307,6 +320,6 @@
                 if (img != null) {
                     boolean switchedDim = false;
-                    if (ExifReader.orientationNeedsCorrection(orientation)) {
-                        if (ExifReader.orientationSwitchesDimensions(orientation)) {
+                    if (ExifReader.orientationNeedsCorrection(entry.getExifOrientation())) {
+                        if (ExifReader.orientationSwitchesDimensions(entry.getExifOrientation())) {
                             width = img.getHeight(null);
                             height = img.getWidth(null);
@@ -314,6 +327,8 @@
                         }
                         final BufferedImage rot = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
-                        final AffineTransform xform = ExifReader.getRestoreOrientationTransform(orientation,
-                                img.getWidth(null), img.getHeight(null));
+                        final AffineTransform xform = ExifReader.getRestoreOrientationTransform(
+                                entry.getExifOrientation(),
+                                img.getWidth(null),
+                                img.getHeight(null));
                         final Graphics2D g = rot.createGraphics();
                         g.drawImage(img, xform, null);
@@ -326,6 +341,6 @@
                     visibleRect = new VisRect(0, 0, width, height);
 
-                    Logging.info("Loaded {0} with dimensions {1}x{2} mem(prev-avail={3}m,taken={4}m) exifOrientationSwitchedDimension={5}",
-                            file.getPath(), width, height, mem/1024/1024, width*height*4/1024/1024, switchedDim);
+                    Logging.info("Loaded {0} with dimensions {1}x{2} memoryTaken={3}m exifOrientationSwitchedDimension={4}",
+                            file.getPath(), width, height, width*height*4/1024/1024, switchedDim);
                 }
 
@@ -361,10 +376,10 @@
 
         private void mouseWheelMovedImpl(int x, int y, int rotation, boolean refreshMousePointInImg) {
-            File file;
+            ImageEntry entry;
             Image image;
             VisRect visibleRect;
 
             synchronized (ImageDisplay.this) {
-                file = ImageDisplay.this.file;
+                entry = ImageDisplay.this.entry;
                 image = ImageDisplay.this.image;
                 visibleRect = ImageDisplay.this.visibleRect;
@@ -418,5 +433,5 @@
 
             synchronized (ImageDisplay.this) {
-                if (ImageDisplay.this.file == file) {
+                if (ImageDisplay.this.entry == entry) {
                     ImageDisplay.this.visibleRect = visibleRect;
                 }
@@ -447,10 +462,10 @@
         public void mouseClicked(MouseEvent e) {
             // Move the center to the clicked point.
-            File file;
+            ImageEntry entry;
             Image image;
             VisRect visibleRect;
 
             synchronized (ImageDisplay.this) {
-                file = ImageDisplay.this.file;
+                entry = ImageDisplay.this.entry;
                 image = ImageDisplay.this.image;
                 visibleRect = ImageDisplay.this.visibleRect;
@@ -486,5 +501,5 @@
 
             synchronized (ImageDisplay.this) {
-                if (ImageDisplay.this.file == file) {
+                if (ImageDisplay.this.entry == entry) {
                     ImageDisplay.this.visibleRect = visibleRect;
                 }
@@ -519,10 +534,10 @@
                 return;
 
-            File file;
+            ImageEntry entry;
             Image image;
             VisRect visibleRect;
 
             synchronized (ImageDisplay.this) {
-                file = ImageDisplay.this.file;
+                entry = ImageDisplay.this.entry;
                 image = ImageDisplay.this.image;
                 visibleRect = ImageDisplay.this.visibleRect;
@@ -539,5 +554,5 @@
                 visibleRect.checkRectPos();
                 synchronized (ImageDisplay.this) {
-                    if (ImageDisplay.this.file == file) {
+                    if (ImageDisplay.this.entry == entry) {
                         ImageDisplay.this.visibleRect = visibleRect;
                     }
@@ -565,10 +580,12 @@
         @Override
         public void mouseReleased(MouseEvent e) {
-            File file;
+            ImageEntry entry;
             Image image;
+            VisRect visibleRect;
 
             synchronized (ImageDisplay.this) {
-                file = ImageDisplay.this.file;
+                entry = ImageDisplay.this.entry;
                 image = ImageDisplay.this.image;
+                visibleRect = ImageDisplay.this.visibleRect;
             }
 
@@ -614,5 +631,5 @@
 
             synchronized (ImageDisplay.this) {
-                if (file == ImageDisplay.this.file) {
+                if (entry == ImageDisplay.this.entry) {
                     if (selectedRect == null) {
                         ImageDisplay.this.visibleRect = visibleRect;
@@ -656,16 +673,16 @@
     /**
      * Sets a new source image to be displayed by this {@code ImageDisplay}.
-     * @param file new source image
-     * @param orientation orientation of new source (landscape, portrait, upside-down, etc.)
+     * @param entry new source image
+     * @since 13220
      */
-    public void setImage(File file, Integer orientation) {
+    public void setImage(ImageEntry entry) {
         synchronized (this) {
-            this.file = file;
+            this.entry = entry;
             image = null;
             errorLoading = false;
         }
         repaint();
-        if (file != null) {
-            new Thread(new LoadImageRunnable(file, orientation), LoadImageRunnable.class.getName()).start();
+        if (entry != null) {
+            new Thread(new LoadImageRunnable(entry), LoadImageRunnable.class.getName()).start();
         }
     }
@@ -682,6 +699,6 @@
     @Override
     public void paintComponent(Graphics g) {
+        ImageEntry entry;
         Image image;
-        File file;
         VisRect visibleRect;
         boolean errorLoading;
@@ -689,5 +706,5 @@
         synchronized (this) {
             image = this.image;
-            file = this.file;
+            entry = this.entry;
             visibleRect = this.visibleRect;
             errorLoading = this.errorLoading;
@@ -699,5 +716,5 @@
 
         Dimension size = getSize();
-        if (file == null) {
+        if (entry == null) {
             g.setColor(Color.black);
             String noImageStr = tr("No image");
@@ -710,7 +727,7 @@
             String loadingStr;
             if (!errorLoading) {
-                loadingStr = tr("Loading {0}", file.getName());
+                loadingStr = tr("Loading {0}", entry.getFile().getName());
             } else {
-                loadingStr = tr("Error on file {0}", file.getName());
+                loadingStr = tr("Error on file {0}", entry.getFile().getName());
             }
             Rectangle2D noImageSize = g.getFontMetrics(g.getFont()).getStringBounds(loadingStr, g);
@@ -742,5 +759,4 @@
                     r.x = visibleRect.x;
                     r.y = visibleRect.y;
-                    System.gc();
                 }
             } else {
@@ -772,5 +788,5 @@
             }
             if (errorLoading) {
-                String loadingStr = tr("Error on file {0}", file.getName());
+                String loadingStr = tr("Error on file {0}", entry.getFile().getName());
                 Rectangle2D noImageSize = g.getFontMetrics(g.getFont()).getStringBounds(loadingStr, g);
                 g.drawString(loadingStr,
@@ -885,10 +901,10 @@
      */
     public void zoomBestFitOrOne() {
-        File file;
+        ImageEntry entry;
         Image image;
         VisRect visibleRect;
 
         synchronized (this) {
-            file = this.file;
+            entry = this.entry;
             image = this.image;
             visibleRect = this.visibleRect;
@@ -911,5 +927,5 @@
 
         synchronized (this) {
-            if (file == this.file) {
+            if (this.entry == entry) {
                 this.visibleRect = visibleRect;
             }
Index: /trunk/src/org/openstreetmap/josm/gui/layer/geoimage/ImageEntry.java
===================================================================
--- /trunk/src/org/openstreetmap/josm/gui/layer/geoimage/ImageEntry.java	(revision 13219)
+++ /trunk/src/org/openstreetmap/josm/gui/layer/geoimage/ImageEntry.java	(revision 13220)
@@ -21,4 +21,5 @@
 import com.drew.metadata.exif.ExifIFD0Directory;
 import com.drew.metadata.exif.GpsDirectory;
+import com.drew.metadata.jpeg.JpegDirectory;
 
 /**
@@ -53,4 +54,7 @@
     private Date gpsTime;
 
+    private int width;
+    private int height;
+
     /**
      * When the correlation dialog is open, we like to show the image position
@@ -77,4 +81,22 @@
 
     /**
+     * Returns width of the image this ImageEntry represents.
+     * @return width of the image this ImageEntry represents
+     * @since 13220
+     */
+    public int getWidth() {
+        return width;
+    }
+
+    /**
+     * Returns height of the image this ImageEntry represents.
+     * @return height of the image this ImageEntry represents
+     * @since 13220
+     */
+    public int getHeight() {
+        return height;
+    }
+
+    /**
      * Returns the position value. The position value from the temporary copy
      * is returned if that copy exists.
@@ -142,5 +164,5 @@
      */
     public Integer getExifOrientation() {
-        return exifOrientation;
+        return exifOrientation != null ? exifOrientation : 1;
     }
 
@@ -228,4 +250,22 @@
             new ThumbsLoader(Collections.singleton(this)).run();
         }
+    }
+
+    /**
+     * Sets the width of this ImageEntry.
+     * @param width set the width of this ImageEntry
+     * @since 13220
+     */
+    public void setWidth(int width) {
+        this.width = width;
+    }
+
+    /**
+     * Sets the height of this ImageEntry.
+     * @param height set the height of this ImageEntry
+     * @since 13220
+     */
+    public void setHeight(int height) {
+        this.height = height;
     }
 
@@ -457,4 +497,5 @@
         }
 
+        final Directory dir = metadata.getFirstDirectoryOfType(JpegDirectory.class);
         final Directory dirExif = metadata.getFirstDirectoryOfType(ExifIFD0Directory.class);
         final GpsDirectory dirGps = metadata.getFirstDirectoryOfType(GpsDirectory.class);
@@ -464,4 +505,16 @@
                 int orientation = dirExif.getInt(ExifIFD0Directory.TAG_ORIENTATION);
                 setExifOrientation(orientation);
+            }
+        } catch (MetadataException ex) {
+            Logging.debug(ex);
+        }
+
+        try {
+            if (dir != null) {
+                // there are cases where these do not match width and height stored in dirExif
+                int width = dir.getInt(JpegDirectory.TAG_IMAGE_WIDTH);
+                int height = dir.getInt(JpegDirectory.TAG_IMAGE_HEIGHT);
+                setWidth(width);
+                setHeight(height);
             }
         } catch (MetadataException ex) {
Index: /trunk/src/org/openstreetmap/josm/gui/layer/geoimage/ImageViewerDialog.java
===================================================================
--- /trunk/src/org/openstreetmap/josm/gui/layer/geoimage/ImageViewerDialog.java	(revision 13219)
+++ /trunk/src/org/openstreetmap/josm/gui/layer/geoimage/ImageViewerDialog.java	(revision 13220)
@@ -323,5 +323,5 @@
                 // Set only if the image is new to preserve zoom and position if the same image is redisplayed
                 // (e.g. to update the OSD).
-                imgDisplay.setImage(entry.getFile(), entry.getExifOrientation());
+                imgDisplay.setImage(entry);
             }
             setTitle(tr("Geotagged Images") + (entry.getFile() != null ? " - " + entry.getFile().getName() : ""));
@@ -356,5 +356,5 @@
             // do not actually show the dialog again with a blank image if currently hidden (fix #10672)
             setTitle(tr("Geotagged Images"));
-            imgDisplay.setImage(null, null);
+            imgDisplay.setImage(null);
             imgDisplay.setOsdText("");
             return;
Index: /trunk/src/org/openstreetmap/josm/gui/tagging/presets/items/KeyedItem.java
===================================================================
--- /trunk/src/org/openstreetmap/josm/gui/tagging/presets/items/KeyedItem.java	(revision 13219)
+++ /trunk/src/org/openstreetmap/josm/gui/tagging/presets/items/KeyedItem.java	(revision 13220)
@@ -100,5 +100,5 @@
          * A set of values that were used for this key.
          */
-        public final SortedSet<String> values = new TreeSet<>();; // NOSONAR
+        public final SortedSet<String> values = new TreeSet<>(); // NOSONAR
         private boolean hadKeys;
         private boolean hadEmpty;
Index: /trunk/tools/pmd/josm-ruleset.xml
===================================================================
--- /trunk/tools/pmd/josm-ruleset.xml	(revision 13219)
+++ /trunk/tools/pmd/josm-ruleset.xml	(revision 13220)
@@ -157,5 +157,4 @@
     <exclude name="ConstructorCallsOverridableMethod"/>
     <exclude name="DataflowAnomalyAnalysis"/>
-    <exclude name="DoNotCallGarbageCollectionExplicitly"/>
     <exclude name="DoNotCallSystemExit"/>
     <exclude name="DontImportSun"/>
Index: /trunk/tools/spotbugs/josm-filter.xml
===================================================================
--- /trunk/tools/spotbugs/josm-filter.xml	(revision 13219)
+++ /trunk/tools/spotbugs/josm-filter.xml	(revision 13220)
@@ -1,51 +1,54 @@
 <FindBugsFilter>
 
-	<Match>
-		<Bug pattern="DM_EXIT" />
-	</Match>
-	<Match>
-		<Bug pattern="DMI_HARDCODED_ABSOLUTE_FILENAME" />
-	</Match>
-	<Match>
-		<Bug pattern="EQ_DOESNT_OVERRIDE_EQUALS" />
-	</Match>
-	<Match>
-		<Bug pattern="IL_INFINITE_LOOP" />
-	</Match>
-	<Match>
-		<Bug pattern="NM_CONFUSING" />
-	</Match>
-	<Match>
-		<Bug pattern="RV_RETURN_VALUE_IGNORED_BAD_PRACTICE" />
-	</Match>
-	<Match>
-		<Bug pattern="SE_BAD_FIELD" />
-	</Match>
-	<Match>
-		<Bug pattern="SE_TRANSIENT_FIELD_NOT_RESTORED" />
-	</Match>
-	<Match>
-		<Bug pattern="UI_INHERITANCE_UNSAFE_GETRESOURCE" />
-	</Match>
-	<Match>
-		<Bug pattern="UWF_FIELD_NOT_INITIALIZED_IN_CONSTRUCTOR" />
-	</Match>
+    <Match>
+        <Bug pattern="DM_EXIT" />
+    </Match>
+    <Match>
+        <Bug pattern="DMI_HARDCODED_ABSOLUTE_FILENAME" />
+    </Match>
+    <Match>
+        <Bug pattern="EQ_DOESNT_OVERRIDE_EQUALS" />
+    </Match>
+    <Match>
+        <Bug pattern="IL_INFINITE_LOOP" />
+    </Match>
+    <Match>
+        <Bug pattern="NM_CONFUSING" />
+    </Match>
+    <Match>
+        <Bug pattern="RV_RETURN_VALUE_IGNORED_BAD_PRACTICE" />
+    </Match>
+    <Match>
+        <Bug pattern="SE_BAD_FIELD" />
+    </Match>
+    <Match>
+        <Bug pattern="SE_TRANSIENT_FIELD_NOT_RESTORED" />
+    </Match>
+    <Match>
+        <Bug pattern="ST_WRITE_TO_STATIC_FROM_INSTANCE_METHOD" />
+    </Match>
+    <Match>
+        <Bug pattern="UI_INHERITANCE_UNSAFE_GETRESOURCE" />
+    </Match>
+    <Match>
+        <Bug pattern="UWF_FIELD_NOT_INITIALIZED_IN_CONSTRUCTOR" />
+    </Match>
 
-	<Match>
-		<Bug pattern="EI_EXPOSE_REP2" />
-		<Class name="org.openstreetmap.josm.tools.CopyList" />
-	</Match>
-	<Match>
-		<Bug pattern="MS_CANNOT_BE_FINAL" />
-		<Class name="org.openstreetmap.josm.Main" />
-	</Match>
-	<Match>
-		<Bug pattern="MS_SHOULD_BE_FINAL" />
-		<Class name="org.openstreetmap.josm.Main" />
-	</Match>
+    <Match>
+        <Bug pattern="EI_EXPOSE_REP2" />
+        <Class name="org.openstreetmap.josm.tools.CopyList" />
+    </Match>
+    <Match>
+        <Bug pattern="MS_CANNOT_BE_FINAL" />
+        <Class name="org.openstreetmap.josm.Main" />
+    </Match>
+    <Match>
+        <Bug pattern="MS_SHOULD_BE_FINAL" />
+        <Class name="org.openstreetmap.josm.Main" />
+    </Match>
 
-	<Match>
-		<Class name="~com.*" />
-	</Match>
+    <Match>
+        <Class name="~com.*" />
+    </Match>
     <Match>
         <Class name="~gnu.getopt.*" />
@@ -54,19 +57,19 @@
         <Class name="~javax.json.*" />
     </Match>
-	<Match>
-		<Class name="~oauth.signpost.*" />
-	</Match>
-	<Match>
-		<Class name="~org.apache.*" />
-	</Match>
+    <Match>
+        <Class name="~oauth.signpost.*" />
+    </Match>
+    <Match>
+        <Class name="~org.apache.*" />
+    </Match>
     <Match>
         <Class name="~org.glassfish.json.*" />
     </Match>
-	<Match>
-		<Class name="~org.jdesktop.swinghelper.debug.*" />
-	</Match>
-	<Match>
-		<Class name="~org.openstreetmap.gui.jmapviewer.*" />
-	</Match>
+    <Match>
+        <Class name="~org.jdesktop.swinghelper.debug.*" />
+    </Match>
+    <Match>
+        <Class name="~org.openstreetmap.gui.jmapviewer.*" />
+    </Match>
     <Match>
         <Class name="~org.openstreetmap.josm.gui.mappaint.mapcss.parsergen.*" />
