Index: trunk/src/org/openstreetmap/josm/gui/MainApplication.java
===================================================================
--- trunk/src/org/openstreetmap/josm/gui/MainApplication.java	(revision 18984)
+++ trunk/src/org/openstreetmap/josm/gui/MainApplication.java	(revision 18985)
@@ -58,4 +58,5 @@
 import javax.swing.JOptionPane;
 import javax.swing.JPanel;
+import javax.swing.JTextPane;
 import javax.swing.KeyStroke;
 import javax.swing.LookAndFeel;
@@ -130,4 +131,5 @@
 import org.openstreetmap.josm.gui.util.RedirectInputMap;
 import org.openstreetmap.josm.gui.util.WindowGeometry;
+import org.openstreetmap.josm.gui.widgets.TextContextualPopupMenu;
 import org.openstreetmap.josm.gui.widgets.UrlLabel;
 import org.openstreetmap.josm.io.CachedFile;
@@ -404,4 +406,38 @@
         askUpdate(tr("Outdated Java WebStart version"), tr("Update to OpenWebStart"), "askUpdateWebStart", /* ICON */"presets/transport/rocket", content, url);
         // CHECKSTYLE.ON: LineLength
+    }
+
+    /**
+     * Tells the user that a sanity check failed
+     * @param title The title of the message to show
+     * @param canContinue {@code true} if the failed sanity check(s) will not instantly kill JOSM when the user edits
+     * @param message The message parts to show the user (as a list)
+     */
+    public static void sanityCheckFailed(String title, boolean canContinue, String... message) {
+        final ExtendedDialog ed;
+        if (canContinue) {
+            ed = new ExtendedDialog(mainFrame, title, tr("Stop"), tr("Continue"));
+            ed.setButtonIcons("cancel", "ok");
+        } else {
+            ed = new ExtendedDialog(mainFrame, title, tr("Stop"));
+            ed.setButtonIcons("cancel");
+        }
+        ed.setDefaultButton(1).setCancelButton(1);
+        // Check if the dialog has not already been permanently hidden by user
+        if (!ed.toggleEnable("sanityCheckFailed").toggleCheckState() || !canContinue) {
+            final String content = Arrays.stream(message).collect(Collectors.joining("</li><li>",
+                    "<html><body><ul><li>", "</li></ul></body></html>"));
+            final JTextPane textField = new JTextPane();
+            textField.setContentType("text/html");
+            textField.setText(content);
+            TextContextualPopupMenu.enableMenuFor(textField, true);
+            ed.setMinimumSize(new Dimension(480, 300));
+            ed.setIcon(JOptionPane.WARNING_MESSAGE);
+            ed.setContent(textField);
+            ed.showDialog();
+        }
+        if (!canContinue || ed.getValue() <= 1) { // 0 == cancel (we want to stop) and 1 == stop
+            Lifecycle.exitJosm(true, -1);
+        }
     }
 
Index: trunk/src/org/openstreetmap/josm/gui/MainInitialization.java
===================================================================
--- trunk/src/org/openstreetmap/josm/gui/MainInitialization.java	(revision 18984)
+++ trunk/src/org/openstreetmap/josm/gui/MainInitialization.java	(revision 18985)
@@ -72,5 +72,6 @@
             new InitializationTask(tr("Starting file watcher"), FileWatcher.getDefaultInstance()::start),
             new InitializationTask(tr("Executing platform startup hook"),
-                    () -> PlatformManager.getPlatform().startupHook(MainApplication::askUpdateJava, MainApplication::askMigrateWebStart)),
+                    () -> PlatformManager.getPlatform().startupHook(MainApplication::askUpdateJava,
+                            MainApplication::askMigrateWebStart, MainApplication::sanityCheckFailed)),
             new InitializationTask(tr("Building main menu"), application::initializeMainWindow),
             new InitializationTask(tr("Updating user interface"), () -> {
Index: trunk/src/org/openstreetmap/josm/tools/PlatformHook.java
===================================================================
--- trunk/src/org/openstreetmap/josm/tools/PlatformHook.java	(revision 18984)
+++ trunk/src/org/openstreetmap/josm/tools/PlatformHook.java	(revision 18985)
@@ -1,4 +1,6 @@
 // License: GPL. For details, see LICENSE file.
 package org.openstreetmap.josm.tools;
+
+import static org.openstreetmap.josm.tools.I18n.tr;
 
 import java.awt.GraphicsEnvironment;
@@ -9,4 +11,5 @@
 import java.io.IOException;
 import java.io.InputStreamReader;
+import java.lang.management.ManagementFactory;
 import java.nio.charset.StandardCharsets;
 import java.security.KeyStoreException;
@@ -15,4 +18,5 @@
 import java.security.cert.X509Certificate;
 import java.text.DateFormat;
+import java.util.ArrayList;
 import java.util.Collection;
 import java.util.Collections;
@@ -87,8 +91,9 @@
       * @param javaCallback Java expiration callback, providing GUI feedback
       * @param webStartCallback WebStart migration callback, providing GUI feedback
-      * @since 17679 (signature)
+      * @since 18985
       */
-    default void startupHook(JavaExpirationCallback javaCallback, WebStartMigrationCallback webStartCallback) {
-        // Do nothing
+    default void startupHook(JavaExpirationCallback javaCallback, WebStartMigrationCallback webStartCallback,
+            SanityCheckCallback sanityCheckCallback) {
+        startupSanityChecks(sanityCheckCallback);
     }
 
@@ -288,4 +293,18 @@
 
     /**
+     * Inform the user that a sanity check or checks failed
+     */
+    @FunctionalInterface
+    interface SanityCheckCallback {
+        /**
+         * Tells the user that a sanity check failed
+         * @param title The title of the message to show
+         * @param canContinue {@code true} if the failed sanity check(s) will not instantly kill JOSM when the user edits
+         * @param message The message parts to show the user (as a list)
+         */
+        void sanityCheckFailed(String title, boolean canContinue, String... message);
+    }
+
+    /**
      * Checks if the running version of Java has expired, proposes to user to update it if needed.
      * @param callback Java expiration callback
@@ -332,5 +351,5 @@
      */
     default String getJavaUrl() {
-        StringBuilder defaultDownloadUrl = new StringBuilder("https://www.azul.com/downloads/?version=java-17-lts");
+        StringBuilder defaultDownloadUrl = new StringBuilder("https://www.azul.com/downloads/?version=java-21-lts");
         if (PlatformManager.isPlatformWindows()) {
             defaultDownloadUrl.append("&os=windows");
@@ -370,4 +389,51 @@
     }
 
+    default void startupSanityChecks(SanityCheckCallback sanityCheckCallback) {
+        final String arch = System.getProperty("os.arch");
+        final List<String> messages = new ArrayList<>();
+        final String jvmArch = System.getProperty("sun.arch.data.model");
+        boolean canContinue = true;
+        if (Utils.getJavaVersion() < 11) {
+            canContinue = false;
+            messages.add(tr("You must update Java to Java {0} or later in order to run this version of JOSM", 17));
+            // Reset webstart/java update prompts
+            Config.getPref().put("askUpdateWebStart", null);
+            Config.getPref().put("askUpdateJava" + Utils.getJavaLatestVersion(), null);
+            Config.getPref().put("askUpdateJavalatest", null);
+        }
+        if (!"x86".equals(arch) && "32".equals(jvmArch)) {
+            messages.add(tr("Please use a 64 bit version of Java -- this will avoid out of memory errors"));
+        }
+        // Note: these might be able to be removed with the appropriate module-info.java settings.
+        final String[] expectedJvmArguments = {
+                "--add-exports=java.base/sun.security.action=ALL-UNNAMED",
+                "--add-exports=java.desktop/com.sun.imageio.plugins.jpeg=ALL-UNNAMED",
+                "--add-exports=java.desktop/com.sun.imageio.spi=ALL-UNNAMED"
+        };
+        final List<String> vmArguments = ManagementFactory.getRuntimeMXBean().getInputArguments();
+        final StringBuilder missingArguments = new StringBuilder();
+        for (String arg : expectedJvmArguments) {
+            if (!vmArguments.contains(arg)) {
+                if (missingArguments.length() > 0) {
+                    missingArguments.append("<br>");
+                }
+                missingArguments.append(arg);
+            }
+        }
+        if (missingArguments.length() > 0) {
+            final String args = missingArguments.toString();
+            messages.add(tr("Missing JVM Arguments:<br>{0}", args));
+        }
+        if (!messages.isEmpty()) {
+            if (canContinue) {
+                sanityCheckCallback.sanityCheckFailed(tr("JOSM may work improperly"), true,
+                        messages.toArray(new String[0]));
+            } else {
+                sanityCheckCallback.sanityCheckFailed(tr("JOSM will be unable to work properly and will exit"), false,
+                        messages.toArray(new String[0]));
+            }
+        }
+    }
+
     /**
      * Called when interfacing with native OS functions. Currently only used with macOS.
Index: trunk/src/org/openstreetmap/josm/tools/PlatformHookOsx.java
===================================================================
--- trunk/src/org/openstreetmap/josm/tools/PlatformHookOsx.java	(revision 18984)
+++ trunk/src/org/openstreetmap/josm/tools/PlatformHookOsx.java	(revision 18985)
@@ -69,5 +69,6 @@
 
     @Override
-    public void startupHook(JavaExpirationCallback javaCallback, WebStartMigrationCallback webStartCallback) {
+    public void startupHook(JavaExpirationCallback javaCallback, WebStartMigrationCallback webStartCallback,
+            SanityCheckCallback sanityCheckCallback) {
         // Here we register callbacks for the menu entries in the system menu and file opening through double-click
         // https://openjdk.java.net/jeps/272
@@ -107,4 +108,5 @@
         checkExpiredJava(javaCallback);
         checkWebStartMigration(webStartCallback);
+        PlatformHook.super.startupHook(javaCallback, webStartCallback, sanityCheckCallback);
     }
 
Index: trunk/src/org/openstreetmap/josm/tools/PlatformHookUnixoid.java
===================================================================
--- trunk/src/org/openstreetmap/josm/tools/PlatformHookUnixoid.java	(revision 18984)
+++ trunk/src/org/openstreetmap/josm/tools/PlatformHookUnixoid.java	(revision 18985)
@@ -64,6 +64,8 @@
 
     @Override
-    public void startupHook(JavaExpirationCallback javaCallback, WebStartMigrationCallback webStartCallback) {
+    public void startupHook(JavaExpirationCallback javaCallback, WebStartMigrationCallback webStartCallback,
+            SanityCheckCallback sanityCheckCallback) {
         checkWebStartMigration(webStartCallback);
+        PlatformHook.super.startupHook(javaCallback, webStartCallback, sanityCheckCallback);
     }
 
Index: trunk/src/org/openstreetmap/josm/tools/PlatformHookWindows.java
===================================================================
--- trunk/src/org/openstreetmap/josm/tools/PlatformHookWindows.java	(revision 18984)
+++ trunk/src/org/openstreetmap/josm/tools/PlatformHookWindows.java	(revision 18985)
@@ -154,8 +154,10 @@
 
     @Override
-    public void startupHook(JavaExpirationCallback javaCallback, WebStartMigrationCallback webStartCallback) {
+    public void startupHook(JavaExpirationCallback javaCallback, WebStartMigrationCallback webStartCallback,
+            SanityCheckCallback sanityCheckCallback) {
         warnSoonToBeUnsupportedJava(javaCallback);
         checkExpiredJava(javaCallback);
         checkWebStartMigration(webStartCallback);
+        PlatformHook.super.startupHook(javaCallback, webStartCallback, sanityCheckCallback);
     }
 
