From 4e472631b06ce3de10a36478bc1520500470f07b Mon Sep 17 00:00:00 2001 From: Lars Vogel Date: Wed, 9 Sep 2026 18:20:33 +0200 Subject: [PATCH] Stop leaking handled statuses in StatusManager MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit A status handed to addLoggedStatus is only removed again once the very same instance comes back through the log listener. That never happens when the listener was not registered because the platform is not running, or when the public addLoggedStatus is called without a following log, so the entry stays for the life of the singleton and keeps its exception and stack trace alive. Hold the entries in a weak set instead. That also drops the Vector, whose per-call locking never made the contains-then-remove pair atomic anyway. Assisted-by: multiple AI agents and layers of automated tooling 🤖 --- .../ui/statushandlers/StatusManager.java | 9 ++-- .../StatusHandlingTestSuite.java | 1 + .../statushandlers/StatusManagerTest.java | 47 +++++++++++++++++++ 3 files changed, 54 insertions(+), 3 deletions(-) create mode 100644 tests/org.eclipse.ui.tests/Eclipse UI Tests/org/eclipse/ui/tests/statushandlers/StatusManagerTest.java diff --git a/bundles/org.eclipse.ui.workbench/eclipseui/org/eclipse/ui/statushandlers/StatusManager.java b/bundles/org.eclipse.ui.workbench/eclipseui/org/eclipse/ui/statushandlers/StatusManager.java index ce14d1684a2..b83c036524f 100644 --- a/bundles/org.eclipse.ui.workbench/eclipseui/org/eclipse/ui/statushandlers/StatusManager.java +++ b/bundles/org.eclipse.ui.workbench/eclipseui/org/eclipse/ui/statushandlers/StatusManager.java @@ -15,8 +15,9 @@ package org.eclipse.ui.statushandlers; -import java.util.List; -import java.util.Vector; +import java.util.Collections; +import java.util.Set; +import java.util.WeakHashMap; import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.ILogListener; import org.eclipse.core.runtime.IStatus; @@ -116,7 +117,9 @@ public class StatusManager { private volatile AbstractStatusHandler statusHandler; - private final List loggedStatuses = new Vector<>(); + // Weakly held so that a status which never reaches the log listener cannot accumulate. + private final Set loggedStatuses = Collections + .newSetFromMap(Collections.synchronizedMap(new WeakHashMap())); private final ListenerList listeners = new ListenerList<>(); diff --git a/tests/org.eclipse.ui.tests/Eclipse UI Tests/org/eclipse/ui/tests/statushandlers/StatusHandlingTestSuite.java b/tests/org.eclipse.ui.tests/Eclipse UI Tests/org/eclipse/ui/tests/statushandlers/StatusHandlingTestSuite.java index bd29e7a6a5a..e283865322e 100644 --- a/tests/org.eclipse.ui.tests/Eclipse UI Tests/org/eclipse/ui/tests/statushandlers/StatusHandlingTestSuite.java +++ b/tests/org.eclipse.ui.tests/Eclipse UI Tests/org/eclipse/ui/tests/statushandlers/StatusHandlingTestSuite.java @@ -29,6 +29,7 @@ SupportTrayTest.class, WorkbenchStatusDialogManagerImplTest.class, WizardsStatusHandlingTestCase.class, + StatusManagerTest.class, }) public class StatusHandlingTestSuite { // diff --git a/tests/org.eclipse.ui.tests/Eclipse UI Tests/org/eclipse/ui/tests/statushandlers/StatusManagerTest.java b/tests/org.eclipse.ui.tests/Eclipse UI Tests/org/eclipse/ui/tests/statushandlers/StatusManagerTest.java new file mode 100644 index 00000000000..6666abf81e1 --- /dev/null +++ b/tests/org.eclipse.ui.tests/Eclipse UI Tests/org/eclipse/ui/tests/statushandlers/StatusManagerTest.java @@ -0,0 +1,47 @@ +/******************************************************************************* + * Copyright (c) 2026 vogella GmbH and others. + * + * This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * which accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: + * Lars Vogel - initial API and implementation + ******************************************************************************/ + +package org.eclipse.ui.tests.statushandlers; + +import java.lang.ref.ReferenceQueue; +import java.lang.ref.WeakReference; + +import org.eclipse.core.runtime.IStatus; +import org.eclipse.core.runtime.Status; +import org.eclipse.ui.statushandlers.StatusManager; +import org.eclipse.ui.tests.leaks.LeakTests; +import org.junit.jupiter.api.Test; + +/** + * Tests that the {@link StatusManager} singleton does not retain the statuses it + * is told about. + */ +public class StatusManagerTest { + + /** + * The entry is only dropped again once the very same instance comes back + * through the log listener, which does not happen for every caller. + */ + @Test + public void testStatusNeverReachingTheLogListenerIsNotRetained() throws Exception { + ReferenceQueue queue = new ReferenceQueue<>(); + IStatus status = new Status(IStatus.ERROR, "org.eclipse.ui.tests", "never logged"); //$NON-NLS-1$ //$NON-NLS-2$ + WeakReference ref = new WeakReference<>(status, queue); + + StatusManager.getManager().addLoggedStatus(status); + status = null; // drop the only strong reference + + LeakTests.checkRef(queue, ref); + } +}