From 604e07b43a725a639ceca6cb1ba62d20e40cdf2d Mon Sep 17 00:00:00 2001 From: hstyi Date: Thu, 20 Feb 2025 17:13:37 +0800 Subject: [PATCH] fix: memory leaks --- .../terminal/panel/vw/AutoRefreshPanel.kt | 42 +++++++++++++++++++ .../panel/vw/NvidiaSMIVisualWindow.kt | 36 +++++----------- .../panel/vw/SystemInformationVisualWindow.kt | 29 +++---------- 3 files changed, 58 insertions(+), 49 deletions(-) create mode 100644 src/main/kotlin/app/termora/terminal/panel/vw/AutoRefreshPanel.kt diff --git a/src/main/kotlin/app/termora/terminal/panel/vw/AutoRefreshPanel.kt b/src/main/kotlin/app/termora/terminal/panel/vw/AutoRefreshPanel.kt new file mode 100644 index 0000000..4b8831a --- /dev/null +++ b/src/main/kotlin/app/termora/terminal/panel/vw/AutoRefreshPanel.kt @@ -0,0 +1,42 @@ +package app.termora.terminal.panel.vw + +import app.termora.Disposable +import kotlinx.coroutines.* +import org.slf4j.LoggerFactory +import javax.swing.JPanel +import kotlin.time.Duration.Companion.milliseconds + +abstract class AutoRefreshPanel : JPanel(), Disposable { + companion object { + private val log = LoggerFactory.getLogger(AutoRefreshPanel::class.java) + } + + protected val coroutineScope = CoroutineScope(Dispatchers.IO) + + protected abstract suspend fun refresh(isFirst: Boolean) + + init { + coroutineScope.launch { + var isFirst = true + while (coroutineScope.isActive) { + try { + refresh(isFirst) + isFirst = false + } catch (e: Exception) { + if (log.isErrorEnabled) { + log.error(e.message, e) + } + if (isFirst) { + break + } + } finally { + delay(1000.milliseconds) + } + } + } + } + + override fun dispose() { + coroutineScope.cancel() + } +} \ No newline at end of file diff --git a/src/main/kotlin/app/termora/terminal/panel/vw/NvidiaSMIVisualWindow.kt b/src/main/kotlin/app/termora/terminal/panel/vw/NvidiaSMIVisualWindow.kt index d18d44e..38b3f7b 100644 --- a/src/main/kotlin/app/termora/terminal/panel/vw/NvidiaSMIVisualWindow.kt +++ b/src/main/kotlin/app/termora/terminal/panel/vw/NvidiaSMIVisualWindow.kt @@ -1,14 +1,13 @@ package app.termora.terminal.panel.vw -import app.termora.I18n -import app.termora.Icons -import app.termora.SSHTerminalTab -import app.termora.SshClients +import app.termora.* import com.formdev.flatlaf.extras.FlatSVGIcon import com.jgoodies.forms.builder.FormBuilder import com.jgoodies.forms.layout.FormLayout -import kotlinx.coroutines.* +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.cancel import kotlinx.coroutines.swing.Swing +import kotlinx.coroutines.withContext import org.apache.commons.lang3.StringUtils import org.jdesktop.swingx.JXBusyLabel import org.slf4j.LoggerFactory @@ -22,7 +21,6 @@ import java.io.StringReader import javax.swing.* import javax.xml.parsers.DocumentBuilderFactory import javax.xml.xpath.XPathFactory -import kotlin.time.Duration.Companion.milliseconds class NvidiaSMIVisualWindow(tab: SSHTerminalTab, visualWindowManager: VisualWindowManager) : SSHVisualWindow(tab, "NVIDIA-SMI", visualWindowManager) { @@ -79,6 +77,8 @@ class NvidiaSMIVisualWindow(tab: SSHTerminalTab, visualWindowManager: VisualWind percentageBtn.icon = if (isPercentage) Icons.text else Icons.percentage nvidiaSMIPanel.refreshPanel() } + + Disposer.register(this, nvidiaSMIPanel) } private data class GPU( @@ -183,8 +183,7 @@ class NvidiaSMIVisualWindow(tab: SSHTerminalTab, visualWindowManager: VisualWind } } - private inner class NvidiaSMIPanel : JPanel(BorderLayout()) { - private val coroutineScope = CoroutineScope(Dispatchers.IO) + private inner class NvidiaSMIPanel : AutoRefreshPanel() { private val xPath by lazy { XPathFactory.newInstance().newXPath() } private val db by lazy { @@ -224,6 +223,8 @@ class NvidiaSMIVisualWindow(tab: SSHTerminalTab, visualWindowManager: VisualWind private fun initViews() { + layout = BorderLayout() + add( FormBuilder.create().debug(false) .layout( @@ -253,27 +254,11 @@ class NvidiaSMIVisualWindow(tab: SSHTerminalTab, visualWindowManager: VisualWind private fun initEvents() { - coroutineScope.launch { - // 首次刷新 - refresh(true) - - while (coroutineScope.isActive) { - delay(1000.milliseconds) - - try { - refresh() - } catch (e: Exception) { - if (log.isErrorEnabled) { - log.error(e.message, e) - } - } - } - } } - private suspend fun refresh(isFirst: Boolean = false) { + override suspend fun refresh(isFirst: Boolean) { val session = tab.getData(SSHTerminalTab.SSHSession) ?: return val doc = try { @@ -347,7 +332,6 @@ class NvidiaSMIVisualWindow(tab: SSHTerminalTab, visualWindowManager: VisualWind refreshPanel() } - } private fun initPanel() { diff --git a/src/main/kotlin/app/termora/terminal/panel/vw/SystemInformationVisualWindow.kt b/src/main/kotlin/app/termora/terminal/panel/vw/SystemInformationVisualWindow.kt index 24ab7b4..917a5a9 100644 --- a/src/main/kotlin/app/termora/terminal/panel/vw/SystemInformationVisualWindow.kt +++ b/src/main/kotlin/app/termora/terminal/panel/vw/SystemInformationVisualWindow.kt @@ -3,8 +3,9 @@ package app.termora.terminal.panel.vw import app.termora.* import com.jgoodies.forms.builder.FormBuilder import com.jgoodies.forms.layout.FormLayout -import kotlinx.coroutines.* +import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.swing.Swing +import kotlinx.coroutines.withContext import org.apache.commons.lang3.StringUtils import org.apache.sshd.client.session.ClientSession import org.slf4j.LoggerFactory @@ -12,7 +13,6 @@ import java.awt.BorderLayout import javax.swing.* import javax.swing.table.DefaultTableCellRenderer import javax.swing.table.DefaultTableModel -import kotlin.time.Duration.Companion.milliseconds class SystemInformationVisualWindow(tab: SSHTerminalTab, visualWindowManager: VisualWindowManager) : @@ -40,9 +40,8 @@ class SystemInformationVisualWindow(tab: SSHTerminalTab, visualWindowManager: Vi Disposer.register(this, systemInformationPanel) } - private inner class SystemInformationPanel : JPanel(BorderLayout()), Disposable { + private inner class SystemInformationPanel : AutoRefreshPanel() { - private val coroutineScope = CoroutineScope(Dispatchers.IO) private val cpuProgressBar = SmartProgressBar() private val memoryProgressBar = SmartProgressBar() @@ -63,6 +62,7 @@ class SystemInformationVisualWindow(tab: SSHTerminalTab, visualWindowManager: Vi private fun initViews() { + layout = BorderLayout() add(createPanel(), BorderLayout.CENTER) } @@ -109,22 +109,10 @@ class SystemInformationVisualWindow(tab: SSHTerminalTab, visualWindowManager: Vi } private fun initEvents() { - coroutineScope.launch { - while (coroutineScope.isActive) { - try { - refresh() - } catch (e: Exception) { - if (log.isErrorEnabled) { - log.error(e.message, e) - } - } finally { - delay(1000.milliseconds) - } - } - } + } - private suspend fun refresh() { + override suspend fun refresh(isFirst: Boolean) { val session = tab.getData(SSHTerminalTab.SSHSession) ?: return try { @@ -144,7 +132,6 @@ class SystemInformationVisualWindow(tab: SSHTerminalTab, visualWindowManager: Vi log.error("refreshDisk", e) } } - } private suspend fun refreshCPUAndMem(session: ClientSession) { @@ -290,10 +277,6 @@ class SystemInformationVisualWindow(tab: SSHTerminalTab, visualWindowManager: Vi } } - override fun dispose() { - coroutineScope.cancel() - } - } private data class Mem(