Electron App 中的事件发射器内存泄漏

Posted

技术标签:

【中文标题】Electron App 中的事件发射器内存泄漏【英文标题】:Event Emitter Memory Leak in Electron App 【发布时间】:2020-10-14 06:43:30 【问题描述】:

我正在开发一个 React Electron 应用程序,我的应用程序的多个区域都存在事件发射器内存泄漏问题。我对 Node.js 不是很有经验,所以请原谅我的业余错误。

(node:3142) MaxListenersExceededWarning: Possible EventEmitter memory leak detected. 11 file:minified listeners added to [EventEmitter]. Use emitter.setMaxListeners() to increase limit

请注意,从长远来看,这给我的应用程序带来了问题,所以这不是我会简单地“解决”或使用setMaxListeners() 保持沉默的问题。

基本上,用户会删除一些文件,然后开始一个链。我创建了一个事件进程,将缩小的文件添加到渲染器进程的列表中:

    对于每个删除的文件,将文件发送到主进程进行修改。 App.js(渲染过程):
document.ondrop = (e) => 
        e.preventDefault();
        e.stopPropagation();
        handleFiles(e.dataTransfer.files);
    ;

const sendFiles = (files) => 
        for (let file of files) 
            file = file.path || file;

            // if file is a folder, recursively check each file in the nested folders
            if (fs.lstatSync(file).isDirectory()) 
                // rename for clarity
                let folder = file;
                let files = fs.readdirSync(folder).map((fileName) => 
                    return path.join(folder, fileName);
                );
                sendFiles(files);
             else 
                // don't add duplicate files to list
                if (list.some((item) => item.oPath === file.path)) 
                    return false;
                
                ipcRenderer.send("file:add", file);
            
        
    ;
    缩小每个单独的文件并将缩小后的路径发送回渲染器进程以进行显示。 main.js(主进程):
ipcMain.on("file:add", (e, path) => 
        minifyFile(path, mainWindow);
    );

const minifyFile = (filePath, mainWindow) => 
...
        mainWindow.webContents.send(
            "file:minified",
            
                path: saveLocation,
                name: name,
                type: extension,
                oSize: originalSize,
                nSize: newSize,
                oPath: filePath,
                newName: newName,
            
        );
...

    现在我们有了要显示的每个缩小文件的文件信息,请将其添加到 React 中的列表状态。 App.js(再次渲染进程):
ipcRenderer.on("file:minified", (e, data) => 
        // if item's path already exists on list, don't add it
        if (list.some((item) => item.path === data.path)) 
            return false;
         else 
            let newList = list;
            newList.push(
                name: data.name,
                path: data.path,
                type: data.type,
                oSize: data.oSize,
                nSize: data.nSize,
                oPath: data.oPath,
                newName: data.newName,
            );

            setList(newList);
            console.log(list);
        
);

发生这种情况时,文件会按预期添加,但随着我在程序的整个生命周期中继续删除更多文件,调用“file:minified”的事件发射器会呈指数增长,这开始在我的程序的其他部分给我带来问题,因为现在它尝试将项目添加到列表中近数百次。

如果有人能帮助我找出我所犯的错误,我将不胜感激,因为我对 Node.js 中的这些问题有点盲目。

这是我需要删除事件侦听器的东西吗?正如我所说,我对这些都不太确定。

我的应用程序的渲染器进程中也出现了另一个内存泄漏问题,任何人有兴趣检查一下是否可以链接它们;我完全不知道。 Here's my other thread.

【问题讨论】:

这通常发生在您挂载订阅事件但在卸载时未取消订阅它们的组件时。您能否更新您的问题以包含您的组件代码的Minimal, Complete, and Reproducible 示例? 请参考这个答案***.com/questions/9768444/… 【参考方案1】:

看起来您每次发送都收到来自主进程的单个响应。如果是这种情况,您可以使用 ipcRenderer.invoke()ipcMain.handle() 之类的...

App.js

document.ondrop = (e) => 
        e.preventDefault();
        e.stopPropagation();
        handleFiles(e.dataTransfer.files);
    ;

const sendFiles = (files) => 
        for (let file of files) 
            file = file.path || file;

            // if file is a folder, recursively check each file in the nested folders
            if (fs.lstatSync(file).isDirectory()) 
                // rename for clarity
                let folder = file;
                let files = fs.readdirSync(folder).map((fileName) => 
                    return path.join(folder, fileName);
                );
                sendFiles(files);
             else 
                // don't add duplicate files to list
                if (list.some((item) => item.oPath === file.path)) 
                    return false;
                
                ipcRenderer.invoke("file:add", file)
                    .then((data) => handleMinified(data))
            
        
    ;

const handleMinified = (data) => 
    // if item's path already exists on list, don't add it
    if (list.some((item) => item.path === data.path)) 
        return false;
         
    else 
        let newList = list;
        newList.push(
            name: data.name,
            path: data.path,
            type: data.type,
            oSize: data.oSize,
            nSize: data.nSize,
            oPath: data.oPath,
            newName: data.newName,
            );

            setList(newList);
            console.log(list);
        

    ;

main.js

ipcMain.handle("file:add", (e, path) => 
        const data = await minifyFile(path);
        return data;
    );


const minifyFile = (filePath, mainWindow) => 
    ...
            return 
                path: saveLocation,
                name: name,
                type: extension,
                oSize: originalSize,
                nSize: newSize,
                oPath: filePath,
                newName: newName,
            

这样,事件侦听器会为您移除 :)

https://www.electronjs.org/docs/api/ipc-renderer#ipcrendererinvokechannel-args

【讨论】:

以上是关于Electron App 中的事件发射器内存泄漏的主要内容,如果未能解决你的问题,请参考以下文章

服务器无法连接到客户端的发射

Android App卡顿慢优化之解决内存抖动及内存泄漏

NodeJS 警告:可能的事件发射器泄漏。添加了 11 个开放式侦听器

Socket.io 聊天应用程序显示检测到 EventEmitter 内存泄漏。增加了 11 位听众。使用发射器.setMaxListeners()

Android App解决卡顿慢之内存抖动及内存泄漏(发现和定位)

Angular 8 组件嵌套在同一个组件事件发射器内