通过 和 工具证明了打开 的时候 .log 会有内存泄漏。

有 .log 的时候,内存是这样的:

去掉之后是这样的:

console.log是什么_console.log_consolelog输出标签

我们得出结论console.log,.log 会导致内存泄漏。

这点没错。

但很多同学会有疑问,是不是因为打开 才有内存泄漏,不打开就不会呢?

这个我测试了一下:点击几次按钮,这时候应该调用了 .log 打印了,然后我过了 10 分钟,确保执行过 gc 了,再打开控制台,依然是可以看到那个对象的详情的。

consolelog输出标签_console.log是什么_console.log

这说明打印的对象没有被 gc,不然怎么还可以看到详情呢?

于是我得出结论,不打开 也是有内存泄漏的。

但我今天换了种测试方法,貌似不打开 时 .log 是没有内存泄漏的。

不打开 怎么确定内存泄漏问题呢?

看下内存大小不就知道了?

通过 .. 是可以拿到堆内存大小的。

我们通过分析 .log 的代码执行后的堆内存大小变化就行。

也就是这样:

<!DOCTYPE html>
<html lang="en">
<body>
    <button id="btn">点我</button>
    <div id="box"></div>  
    <script>
        const btn = document.getElementById('btn');
        const box = document.getElementById('box');

        btn.addEventListener('click'function({
            const MB = 1024 * 1024;
            log();

            function log({
                const memory = performance.memory.totalJSHeapSize;
                const usagedMemory = Math.floor(memory / MB);
                box.insertAdjacentHTML('beforeend'`${usagedMemory} `);

                const obj = {usagedMemory, str'g'.repeat(50 * MB)};
                console.log(obj); 

                setTimeout(() => log(), 50);
            }
        });
    
</script>
</body>
</html>

按钮点击的时候,拿到当前堆内存的大小。然后打印一个大字符串和堆内存大小。

因为我们看不到控制台,所以也会加到 dom 中来显示。

通过定时器不断地执行这样的操作。

我们先打开 测试下:

consolelog输出标签_console.log_console.log是什么

可以看到每次打印后内存都在增长,并且在内存达到 4G 的时候就崩溃了。

说明 .log 确实存在内存泄漏。

那我们再关掉 测试下:

console.log是什么_console.log_consolelog输出标签

内存一直稳定不变,说明函数执行完之后,作用域销毁,打印的对象就被销毁了,没有内存泄漏。

我们过程中打开 测试下:

consolelog输出标签_console.log_console.log是什么

可以看到一打开 ,再次执行 .log 的时候,内存就增长了,说明这时候内存泄漏了。

那如果我先打开 ,然后再关掉呢?

consolelog输出标签_console.log是什么_console.log

可以看到,只要关闭了 ,内存就稳定了。但之前打印的对象依然被引用着,那部分内存不会被释放。

这样,我们就可以得出结论:不打开 的时候,.log 不会内存泄漏。

(但我感觉我之前的测试方式也没错呀,不知道哪里有问题)

还有同学问,那如果直接打印字符串呢?

我们直接打印字符串试一下:

consolelog输出标签_console.log_console.log是什么

consolelog输出标签_console.log_console.log是什么

可以看到,内存也是平稳的。

为什么呢?字符串不也是对象、可以看到详情的吗?

这是因为字符串比较特殊,有个叫做常量池的东西。

录制一下内存快照:

consolelog输出标签_console.log是什么_console.log

看一下字符串占用的内存:

console.log_consolelog输出标签_console.log是什么

是 @91 的地址。

我过了一段时间再录制了一次快照,依然只有一个字符串,地址是 @91。

这就是字符串常量池的作用,同样的字符串只会创建一次,减少了相同字符串的内存占用。

但 还有另一种创建方式:new

这种方式就不一样了:

consolelog输出标签_console.log是什么_console.log

这时候创建的是一个堆中的对象,然后引用了常量池中的 。

console.log_consolelog输出标签_console.log是什么

这也是为啥字符串字面量是 ,而 new 是 :

因为会不断在堆中创建对象,所以这时候 .log 的内存泄漏依然会使堆内存上升:

那 node.js 的 .log 有没有内存泄漏呢?

我们也用同样的方式测试下就好了,只是这时候拿到内存数据是用 .() 的 api:

const MB = 1024 * 1024;

log();

function log({
    const memory = process.memoryUsage().heapUsed
    const usagedMemory = Math.floor(memory / MB);

    const obj = { usagedMemory, obj'g'.repeat(50 * MB) };
    console.log(obj); 

    setTimeout(() => log(), 50);
}

执行一下:

console.log是什么_consolelog输出标签_console.log

可以看到内存是稳定的,并不会内存泄漏。

这是因为 node 打印的是序列化以后的对象,并不是对象引用。

总结

.log 在 打开的时候是有内存泄漏的,因为控制台打印的是对象引用。但是不打开 是不会有内存泄漏的。

我们通过打印内存占用大小的方式来证明了这一点。

因为常量池的存在,同样的字符串只会创建一次。new 的话才会在堆中创建一个对象,然后指向常量池中的字符串字面量。

此外, 打印的是序列化以后的对象,所以是没有内存泄漏的。

所以console.log,生产环境也是可以用 .log 的,没有内存泄漏问题。


限时特惠:
本站持续每日更新海量各大内部创业课程,一年会员仅需要98元,全站资源免费下载
点击查看详情

站长微信:Jiucxh

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注