【程序员日记】一行console.log引发的血案

▒ 目录 ▒

    • 导读
      • 需求
      • 开发环境
    • 1️⃣ 艰难的排查过程
      • 1. 程序闪退
      • 2. 确定为内存泄漏
      • 3. 误入歧途
      • 4. 二分法注释代码
      • 5. 猿脑猜想
    • 2️⃣ 排查
      • procexp.exe
      • Performance 和 Memory
    • 3️⃣ 剔除生产环境中的console.log
      • webpack插件terser-webpack-plugin
    • 文章小结
    • 参考资料

导读

需求

最近公司使用electron中出现内存泄漏的情况。经过n天的努力,最后定位到console.log的问题。
这好像不是第一次遇到类似的问题,特此记录,以敬后效。

开发环境

版本号 描述
文章日期 2023-11-04
操作系统 Win10 - 22H2 19045.3570

1️⃣ 艰难的排查过程

1. 程序闪退

开发过程中程序一切运行正常,到了测试那里,出现崩溃情况。
多次尝试发现半个小时左右就崩溃了,多个电脑都是这样的。

2. 确定为内存泄漏

一开始一直以为是自己开发的node模块代码异常,导致崩溃。
后来看了下任务管理器的内存,发现某个render进程内存螺旋式上升,积累到半个小时候就崩溃了。
结论:

  • 内存泄漏导致崩溃
  • 大概率不是node模块导致的,因为是render进程不断的增大。

3. 误入歧途

排查问题最笨的思路就是注释代码。由于各种原因,我们也是这条路排查的。
当我们注释掉某封包逻辑后发现一切运行正常。于是猜测是封包的库导致的问题,当即想的是换个库试试。

4. 二分法注释代码

后来某大佬说,可能是用法不对,逐步注释代码试试。然后二分法注释代码,最后在WebSocket的某回调函数内发现,注释了console.log,让一切恢复正常了。

5. 猿脑猜想

该代码会将接受的内容(3Mb左右的对象)打印出来,最后发现该对象未被释放。至于为何未释放,这里也只留下几个猜想:

  • WebSocket回调函数对该对象进行了引用,导致GC不能释放。
  • WebSocket回调函数对该对象进行了缓存(方便后续调用),缓存队列巨大(上千的长度),chrome内存是有上限的(各个版本不一样的,我们用的x86的,据说在2G左右),达到上限的时候,还未出发其内存回收。

2️⃣ 排查

procexp.exe

作为专业的windows工具procexp.exe,提供了查看内存变换的功能,我们可以通过它看到一些内存上的规律。
小编遇到的问题,可以在这上面发现,每2秒,内存增加3Mb左右。这个大小刚好是我们传递的某个对象,通过它能快速定位问题。

procexp.exe默认没有显示Private Delta Bytes选项,我们通过下面的步骤打开该列。

  1. 右键表头,打开对话框
    在这里插入图片描述
    2.选择Process Memory标签页,然后选择Private Delta Bytes选项
    【程序员日记】一行console.log引发的血案_第1张图片
  • 模拟内存增长:
    如下图,不断的执行window.___=[]; ___.push(new Array(2*1000*1000));,可以查看到内存增长!
    【程序员日记】一行console.log引发的血案_第2张图片

ps: windows自带的任务管理器也有类似的功能(貌似win11后的某个新版本才有的)。

Performance 和 Memory

这个操作就很专业了,具体细节很多,小编发现一篇很不错的文章:《console.log 一定会导致内存泄漏? 》https://juejin.cn/post/7185501830040944698,这里就不画蛇添足了,有兴趣的直接看该文章即可。

3️⃣ 剔除生产环境中的console.log

webpack插件terser-webpack-plugin

erser-webpack-plugin 是一个 Webpack 的插件,它主要用于对 JavaScript 代码进行压缩和优化。
它通过使用 Terser 这个 JavaScript 压缩库,来实现对 JavaScript 代码的压缩和优化。Terser 是一个非常强大的压缩库,它能够减小 JavaScript 代码的体积,并提高代码的运行效率。

terser-webpack-plugin 插件的使用非常简单,只需要在 Webpack 的配置中添加这个插件,并设置相关的选项即可。可以进行如下配置,保证生成环境中不包含console系列函数:

module.exports = {
  optimization: {
    minimizer: [
      new TerserPlugin({
        terserOptions: {
          // 你的压缩选项
          compress: {
            // drop_console为true时,将删除所有console的函数
            drop_console: process.env.NODE_ENV==='production',
            drop_debugger: true,
            pure_funcs: ["console.log", "console.warn"]
          }
        }
      }),
    ],
  },
};

文章小结

首先,我们需要明确一点,console.log 本身并不会造成内存泄漏。内存泄漏是指在程序运行过程中,一些不再需要的对象或资源仍然被占用,导致内存无法被释放,从而导致程序运行缓慢或崩溃。

我们来分析一下可能导致内存泄漏的原因。主要有以下几种可能:

  • 闭包:如果在 console.log 中使用了闭包,并且该闭包中引用了外部的变量或对象,那么这些变量或对象就不会被垃圾回收器回收,从而导致内存泄漏。
  • 对象池:如果在代码中使用了对象池技术,例如Pool 或cache,那么这些对象可能会被长期占用,从而导致内存泄漏。
  • 循环引用:如果在代码中存在两个或多个对象之间的循环引用,例如A引用了B,B引用了A,那么这些对象就不会被垃圾回收器回收,从而导致内存泄漏。

综上所述,console.log 本身并不会造成内存泄漏。但是,如果在代码中使用了不当的闭包、对象池或循环引用,那么这些因素可能会导致内存泄漏。
因此,在编写代码时,我们需要注意避免这些问题,以保证程序的稳定性和性能。而且尽可能的别让 console.log 上生产!

参考资料

  • console.log 一定会导致内存泄漏?不打开 devtools 就不会 https://juejin.cn/post/7185501830040944698
  • 千万别让 console.log 上生产! https://juejin.cn/post/7185128318235541563

ps: 文章中内容仅用于技术交流,请勿用于违规违法行为。

你可能感兴趣的:(#,Javascript随笔,javascript,console.log,程序员日记)