Perf(custom): add memory monitor and optimiz memory usage

This commit is contained in:
Kuingsmile
2025-08-13 15:11:55 +08:00
parent 11341c024a
commit f0b0e55937
7 changed files with 144 additions and 79 deletions

View File

@@ -62,8 +62,8 @@ const trayWindowOptions = {
preload: preloadPath, preload: preloadPath,
nodeIntegration: false, nodeIntegration: false,
contextIsolation: true, contextIsolation: true,
nodeIntegrationInWorker: true, nodeIntegrationInWorker: false,
backgroundThrottling: false, backgroundThrottling: true,
webSecurity: false webSecurity: false
} }
} }
@@ -83,11 +83,11 @@ const settingWindowOptions = {
webPreferences: { webPreferences: {
sandbox: false, sandbox: false,
webviewTag: true, webviewTag: true,
backgroundThrottling: false, backgroundThrottling: true,
preload: preloadPath, preload: preloadPath,
nodeIntegration: false, nodeIntegration: false,
contextIsolation: true, contextIsolation: true,
nodeIntegrationInWorker: true, nodeIntegrationInWorker: false,
webSecurity: false webSecurity: false
} }
} as IBrowserWindowOptions } as IBrowserWindowOptions
@@ -112,8 +112,8 @@ const miniWindowOptions = {
preload: preloadPath, preload: preloadPath,
nodeIntegration: false, nodeIntegration: false,
contextIsolation: true, contextIsolation: true,
backgroundThrottling: false, backgroundThrottling: true,
nodeIntegrationInWorker: true nodeIntegrationInWorker: false
} }
} as IBrowserWindowOptions } as IBrowserWindowOptions
@@ -133,7 +133,7 @@ const renameWindowOptions = {
preload: preloadPath, preload: preloadPath,
nodeIntegration: false, nodeIntegration: false,
contextIsolation: true, contextIsolation: true,
nodeIntegrationInWorker: true, nodeIntegrationInWorker: false,
backgroundThrottling: false backgroundThrottling: false
} }
} as IBrowserWindowOptions } as IBrowserWindowOptions
@@ -158,11 +158,11 @@ const toolboxWindowOptions = {
icon: logo, icon: logo,
webPreferences: { webPreferences: {
sandbox: false, sandbox: false,
backgroundThrottling: false, backgroundThrottling: true,
preload: preloadPath, preload: preloadPath,
nodeIntegration: false, nodeIntegration: false,
contextIsolation: true, contextIsolation: true,
nodeIntegrationInWorker: true, nodeIntegrationInWorker: false,
webSecurity: false webSecurity: false
} }
} as IBrowserWindowOptions } as IBrowserWindowOptions

View File

@@ -1,5 +1,31 @@
import { EventEmitter } from 'node:events' import { EventEmitter } from 'node:events'
const bus = new EventEmitter() class OptimizedBus extends EventEmitter {
constructor () {
export default bus super()
this.setMaxListeners(50)
}
once (event: string | symbol, listener: (...args: any[]) => void): this {
const wrappedListener = (...args: any[]) => {
try {
listener(...args)
} finally {
this.removeListener(event, wrappedListener)
}
}
return super.once(event, wrappedListener)
}
cleanupListeners () {
const events = this.eventNames()
events.forEach(event => {
const listenerCount = this.listenerCount(event)
console.log(` listener count (${listenerCount}) for event: ${String(event)}`)
})
}
}
const bus = new OptimizedBus()
export default bus

View File

@@ -34,6 +34,7 @@ import { II18nLanguage, IRemoteNoticeTriggerHook, ISartMode, IWindowList } from
import { getUploadFiles } from '~/utils/handleArgv' import { getUploadFiles } from '~/utils/handleArgv'
import { initI18n } from '~/utils/handleI18n' import { initI18n } from '~/utils/handleI18n'
import { notificationList } from '~/utils/notification' import { notificationList } from '~/utils/notification'
import { MemoryMonitor } from '~/utils/performanceOptimizer'
import { CLIPBOARD_IMAGE_FOLDER } from '~/utils/static' import { CLIPBOARD_IMAGE_FOLDER } from '~/utils/static'
import updateChecker from '~/utils/updateChecker' import updateChecker from '~/utils/updateChecker'
@@ -164,6 +165,10 @@ class LifeCycle {
initI18n() initI18n()
rpcServer.start() rpcServer.start()
busEventList.listen() busEventList.listen()
if (process.env.NODE_ENV === 'development') {
MemoryMonitor.start(30000)
}
} }
#onReady () { #onReady () {
@@ -315,6 +320,7 @@ class LifeCycle {
server.shutdown() server.shutdown()
webServer.stop() webServer.stop()
stopFileServer() stopFileServer()
MemoryMonitor.stop()
}) })
// Exit cleanly on request from parent process in development mode. // Exit cleanly on request from parent process in development mode.
if (isDevelopment) { if (isDevelopment) {

View File

@@ -15,7 +15,7 @@ class ClipboardWatcher extends EventEmitter {
this.timer = null this.timer = null
} }
startListening (watchDelay = 500) { startListening (watchDelay = 1000) {
this.stopListening(false) this.stopListening(false)
this.timer = setInterval(() => { this.timer = setInterval(() => {

View File

@@ -70,7 +70,6 @@ export const showNotification = (
const notification = new Notification({ const notification = new Notification({
title: options.title, title: options.title,
body: options.body body: options.body
// icon: options.icon || undefined
}) })
const handleClick = () => { const handleClick = () => {
if (options.clickToCopy) { if (options.clickToCopy) {

View File

@@ -0,0 +1,31 @@
export class MemoryMonitor {
// eslint-disable-next-line no-undef
private static interval: NodeJS.Timeout | null = null
static start (intervalMs: number = 30000) {
if (this.interval) return
this.interval = setInterval(() => {
const memUsage = process.memoryUsage()
const mbUsage = {
rss: Math.round(memUsage.rss / 1024 / 1024),
heapTotal: Math.round(memUsage.heapTotal / 1024 / 1024),
heapUsed: Math.round(memUsage.heapUsed / 1024 / 1024),
external: Math.round(memUsage.external / 1024 / 1024)
}
console.log(`[Memory] RSS: ${mbUsage.rss}MB, Heap: ${mbUsage.heapUsed}/${mbUsage.heapTotal}MB, External: ${mbUsage.external}MB`)
if (mbUsage.heapUsed / mbUsage.heapTotal > 0.8 && global.gc) {
console.log('[Memory] Triggering garbage collection')
global.gc()
}
}, intervalMs)
}
static stop () {
if (this.interval) {
clearInterval(this.interval)
this.interval = null
}
}
}

View File

@@ -1,63 +1,66 @@
import db from '@core/datastore' import db from '@core/datastore'
import windowManager from 'apis/app/window/windowManager' import windowManager from 'apis/app/window/windowManager'
import { screen } from 'electron' import { screen } from 'electron'
import { configPaths } from '~/utils/configPaths' import { configPaths } from '~/utils/configPaths'
import { IWindowList } from '~/utils/enum' import { IWindowList } from '~/utils/enum'
export function openMiniWindow (hideSettingWindow: boolean = true) { export function openMiniWindow (hideSettingWindow: boolean = true) {
const miniWindow = windowManager.get(IWindowList.MINI_WINDOW)! const miniWindow = windowManager.get(IWindowList.MINI_WINDOW)!
miniWindow.removeAllListeners()
if (db.get(configPaths.settings.miniWindowOntop)) { miniWindow.removeAllListeners('close')
miniWindow.setAlwaysOnTop(true) miniWindow.removeAllListeners('move')
}
const { width, height } = screen.getPrimaryDisplay().workAreaSize if (db.get(configPaths.settings.miniWindowOntop)) {
const lastPosition = db.get(configPaths.settings.miniWindowPosition) miniWindow.setAlwaysOnTop(true)
const setPositionFunc = () => { }
const position = miniWindow.getPosition() const { width, height } = screen.getPrimaryDisplay().workAreaSize
db.set(configPaths.settings.miniWindowPosition, position) const lastPosition = db.get(configPaths.settings.miniWindowPosition)
} const setPositionFunc = () => {
if (lastPosition) { const position = miniWindow.getPosition()
if (lastPosition[0] < 0 || lastPosition[0] > width || lastPosition[1] < 0 || lastPosition[1] > height) { db.set(configPaths.settings.miniWindowPosition, position)
miniWindow.setPosition(width - 100, height - 100) }
db.set(configPaths.settings.miniWindowPosition, [width - 100, height - 100]) if (lastPosition) {
} else if (lastPosition[0] + miniWindow.getSize()[0] > width || lastPosition[1] + miniWindow.getSize()[1] > height) { if (lastPosition[0] < 0 || lastPosition[0] > width || lastPosition[1] < 0 || lastPosition[1] > height) {
miniWindow.setPosition(width - miniWindow.getSize()[0], height - miniWindow.getSize()[1]) miniWindow.setPosition(width - 100, height - 100)
db.set(configPaths.settings.miniWindowPosition, [width - miniWindow.getSize()[0], height - miniWindow.getSize()[1]]) db.set(configPaths.settings.miniWindowPosition, [width - 100, height - 100])
} else { } else if (lastPosition[0] + miniWindow.getSize()[0] > width || lastPosition[1] + miniWindow.getSize()[1] > height) {
miniWindow.setPosition(lastPosition[0], lastPosition[1]) miniWindow.setPosition(width - miniWindow.getSize()[0], height - miniWindow.getSize()[1])
} db.set(configPaths.settings.miniWindowPosition, [width - miniWindow.getSize()[0], height - miniWindow.getSize()[1]])
} else { } else {
miniWindow.setPosition(width - 100, height - 100) miniWindow.setPosition(lastPosition[0], lastPosition[1])
} }
} else {
miniWindow.on('close', setPositionFunc) miniWindow.setPosition(width - 100, height - 100)
miniWindow.on('move', setPositionFunc) }
miniWindow.show()
miniWindow.focus() miniWindow.on('close', setPositionFunc)
if (hideSettingWindow) { miniWindow.on('move', setPositionFunc)
const settingWindow = windowManager.get(IWindowList.SETTING_WINDOW)! miniWindow.show()
settingWindow.hide() miniWindow.focus()
} else { if (hideSettingWindow) {
const autoCloseMainWindow = db.get(configPaths.settings.autoCloseMainWindow) || false const settingWindow = windowManager.get(IWindowList.SETTING_WINDOW)!
if (windowManager.has(IWindowList.SETTING_WINDOW) && autoCloseMainWindow) { settingWindow.hide()
windowManager.get(IWindowList.SETTING_WINDOW)!.hide() } else {
} const autoCloseMainWindow = db.get(configPaths.settings.autoCloseMainWindow) || false
} if (windowManager.has(IWindowList.SETTING_WINDOW) && autoCloseMainWindow) {
} windowManager.get(IWindowList.SETTING_WINDOW)!.hide()
}
export const openMainWindow = () => { }
const settingWindow = windowManager.get(IWindowList.SETTING_WINDOW) }
const autoCloseMiniWindow = db.get(configPaths.settings.autoCloseMiniWindow) || false
settingWindow!.show() export const openMainWindow = () => {
settingWindow!.focus() const settingWindow = windowManager.get(IWindowList.SETTING_WINDOW)
if (windowManager.has(IWindowList.MINI_WINDOW) && autoCloseMiniWindow) { const autoCloseMiniWindow = db.get(configPaths.settings.autoCloseMiniWindow) || false
windowManager.get(IWindowList.MINI_WINDOW)!.hide() settingWindow!.show()
} settingWindow!.focus()
} if (windowManager.has(IWindowList.MINI_WINDOW) && autoCloseMiniWindow) {
windowManager.get(IWindowList.MINI_WINDOW)!.hide()
export const hideMiniWindow = () => { }
if (windowManager.has(IWindowList.MINI_WINDOW)) { }
windowManager.get(IWindowList.MINI_WINDOW)!.hide()
} export const hideMiniWindow = () => {
} if (windowManager.has(IWindowList.MINI_WINDOW)) {
windowManager.get(IWindowList.MINI_WINDOW)!.hide()
}
}