Index: trunk/src/org/openstreetmap/josm/gui/layer/AbstractTileSourceLayer.java
===================================================================
--- trunk/src/org/openstreetmap/josm/gui/layer/AbstractTileSourceLayer.java	(revision 8859)
+++ trunk/src/org/openstreetmap/josm/gui/layer/AbstractTileSourceLayer.java	(revision 8860)
@@ -151,7 +151,7 @@
      *  in MapView (for example - when limiting min zoom in imagery)
      *
-     *  Use static instance so memory is shared between layers to prevent out of memory exceptions, when user is working with many layers
-     */
-    protected static TileCache tileCache = new MemoryTileCache(MEMORY_CACHE_SIZE.get());
+     *  Use per-layer tileCache instance, as the more layers there are, the more tiles needs to be cached
+     */
+    protected TileCache tileCache; // initialized together with tileSource
     protected AbstractTMSTileSource tileSource;
     protected TileLoader tileLoader;
@@ -166,4 +166,8 @@
         this.setVisible(true);
         MapView.addZoomChangeListener(this);
+        this.tileSource = getTileSource(info);
+        if (this.tileSource == null) {
+            throw new IllegalArgumentException(tr("Failed to create tile source"));
+        }
     }
 
@@ -207,4 +211,6 @@
         if (tileLoader == null)
             tileLoader = new OsmTileLoader(this, headers);
+
+        tileCache = new MemoryTileCache(estimateTileCacheSize());
     }
 
@@ -506,9 +512,5 @@
     @Override
     public void hookUpMapView() {
-        this.tileSource = getTileSource(info);
-        if (this.tileSource == null) {
-            throw new IllegalArgumentException(tr("Failed to create tile source"));
-        }
-
+        super.hookUpMapView();
         projectionChanged(null, Main.getProjection()); // check if projection is supported
         initTileSource(this.tileSource);
@@ -660,4 +662,23 @@
     }
 
+    @Override
+    protected long estimateMemoryUsage() {
+        return 4L * tileSource.getTileSize() * tileSource.getTileSize() * estimateTileCacheSize();
+    }
+
+    protected int estimateTileCacheSize() {
+        int height = (int) Toolkit.getDefaultToolkit().getScreenSize().getHeight();
+        int width = (int) Toolkit.getDefaultToolkit().getScreenSize().getWidth();
+        int tileSize = 256; // default tile size
+        if (tileSource != null) {
+            tileSize = tileSource.getTileSize();
+        }
+        // as we can see part of the tile at the top and at the bottom, use Math.ceil(...) + 1 to accommodate for that
+        int visibileTiles = (int) (Math.ceil( (double)height / tileSize + 1) * Math.ceil((double)width / tileSize + 1));
+        // add 10% for tiles from different zoom levels
+        return (int)Math.ceil(
+                Math.pow(2d, ZOOM_OFFSET.get()) * visibileTiles // use offset to decide, how many tiles are visible
+                * 2);
+    }
     /**
      * Checks zoom level against settings
@@ -1510,7 +1531,8 @@
             myDrawString(g, tr("Pixel scale: {0}", getScaleFactor(currentZoomLevel)), 50, 170);
             myDrawString(g, tr("Best zoom: {0}", getBestZoom()), 50, 185);
+            myDrawString(g, tr("Estimated cache size: {0}", estimateTileCacheSize()), 50, 200);
             if (tileLoader instanceof TMSCachedTileLoader) {
                 TMSCachedTileLoader cachedTileLoader = (TMSCachedTileLoader) tileLoader;
-                int offset = 185;
+                int offset = 200;
                 for (String part: cachedTileLoader.getStats().split("\n")) {
                     myDrawString(g, tr("Cache stats: {0}", part), 50, offset += 15);
Index: trunk/src/org/openstreetmap/josm/gui/layer/Layer.java
===================================================================
--- trunk/src/org/openstreetmap/josm/gui/layer/Layer.java	(revision 8859)
+++ trunk/src/org/openstreetmap/josm/gui/layer/Layer.java	(revision 8860)
@@ -138,6 +138,26 @@
      * not execute code in the constructor, that assumes Main.map.mapView is
      * not null. Instead override this method.
+     *
+     * This implementation provides check, if JOSM will be able to use Layer. Layers
+     * using a lot of memory, which do know in advance, how much memory they use, should
+     * override {@link #estimateMemoryUsage() estimateMemoryUsage} method and give a hint.
+     *
+     * This allows for preemptive warning message for user, instead of failing later on
+     *
+     * Remember to call {@code super.hookUpMapView()} when overriding this method
      */
     public void hookUpMapView() {
+        // calculate total memory needed for all layers
+        long memoryBytesRequired = 50 * 1024 * 1024; // assumed minimum JOSM memory footprint
+        if (Main.map != null && Main.map.mapView != null) {
+            for (Layer layer: Main.map.mapView.getAllLayers()) {
+                memoryBytesRequired += layer.estimateMemoryUsage();
+            }
+            if (memoryBytesRequired >  Runtime.getRuntime().maxMemory()) {
+                throw new IllegalArgumentException(tr("To add another layer you need to allocate at least {0,number,#}MB memory to JOSM using -Xmx{0,number,#}M "
+                        + "option (see http://forum.openstreetmap.org/viewtopic.php?id=25677).\n"
+                        + "Currently you have {1,number,#}MB memory allocated for JOSM", memoryBytesRequired / 1024 / 1024, Runtime.getRuntime().maxMemory() / 1024 / 1024));
+            }
+        }
     }
 
@@ -507,3 +527,10 @@
         return SaveActionBase.createAndOpenSaveFileChooser(tr("Save Layer"), "lay");
     }
+
+    /**
+     * @return bytes that the tile will use. Needed for resource management
+     */
+    protected long estimateMemoryUsage() {
+        return 0;
+    }
 }
