求教关于js内存释放的问题。

发布于 2022-09-12 03:14:05 字数 1214 浏览 20 评论 0

最近在写一个chrome插件
有一个简单的功能分2步是这样:

  1. 从站点提取链接遍历所有详情页面(目前2000+ 每天几十个递增),提取关键数据生成数组对象。
  2. 从生成数组对象判断是否有other_href字段,如果有的话,请求这个other_href解析返回的html获得需要的数据,保存在对象上。

上面2步的请求都是用axios.get,请求拿到的result.data只进入一个解析函数返回解析对象,没有他用。
现在遇到的问题是第二步的other_href是从第一步的result.data解析出来,可能是这个原因导致第一步请求回来的html不能从内存中释放,只开1000多个页面插件就因为内存溢出崩溃了。
打开内存快照里面堆满了<!DOCTYPE html> ...,我测试过如果不对这个other_href进行处理,内存会自动释放维持在300M左右。
我的解析函数只是对字符串进行截取处理

function parseDetailsHtml(html) {

  function getOthersHref(htm) {
    const index = htm.indexOf('name="description"'),
      lastIndex = htm.indexOf('<', index),
      match = (htm.substring(index, lastIndex + 1) || '').match(/>(.+?)</) || [];
    return (match[1] || '').trim();
  }

  //... 其他提取函数

  return {
    others_href: getOthersHref(html),
    ...// 同getOthersHref的[字段名]:[函数名](html)
  };
}

难道这样也能产生引用造成不能释放吗?

现在完全搞不懂应该怎么做了。
111111.png
2222222.png

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

久隐师 2022-09-19 03:14:05

还真有可能是返回的字符串在底层对原字符串进行了引用导致未被 GC。参见这篇文章 奇技淫巧学 V8 之六,字符串在 V8 内的表达,文中提到,对原 string 进行 substring/slice 底层依然会保留完整的原字符串在堆上的。

就这个问题有一个办法可以规避,用 String.fromCharCode(str.charCodeAt(i)) 转成每个字符的 unicode 再转回去。

就这个问题有一个办法可以规避,可以获取每个字符然后重新造字符串来消除底层 SliceString 的结构。

  function getOthersHref(htm) {
    const index = htm.indexOf('name="description"'),
      lastIndex = htm.indexOf('<', index),
      match = (htm.substring(index, lastIndex + 1) || '').match(/>(.+?)</) || [];
    return cloneStr((match[1] || '').trim());
  }

  //function cloneStr(str) {
  //   let copied = '';
  //   for(let i=0; i<str.length; i++) {
  //      copied += String.fromCharCode(str.charCodeAt(i));
  //   }
  //   return copied;
  //}
  const cloneStr = str => [...str].join('');

还好你的 href 也不会很长,这样并不会影响性能。


做一个实验,证明 slice substring match trim 后最终返回的字符串仍然在底层引用了原字符串。

批注 2020-06-13 230909.jpg

clone 后貌似还有俩内部的 regexp_last_match_info 在引用者。

image.png

随便运行一个正则,没了,貌似是系统内部保存了最后一次正则相关的内容?

image.png

爱的故事 2022-09-19 03:14:05

...我看到你的对象占用的内存仅比字符串的大一点,这说明其实这个字符串是存在于对象里的,之所以字符串没被释放,是你的对象没被释放啊.window的内存看着也是比object大一点,这样就很明了了 string挂在object上,object挂在window上,所以都不能释放.

// 二次修改
image.png
指的是这里,这里的值代表如果对象被释放将会释放出多少内存.
明显它们都彼此依赖,而最底层是string,因为他的卷影大小跟保留大小是一致的,代表它它实际占用跟可被释放的内存大小是一致的.

但是令我疑惑的是string的距离竟然24,这代表者它上面还有很多层,他是通过多层依赖存活下来的.

题主采纳了上一位答者的答案,我看了之后验证,的确是有一定的道理,slicestring的确产生引用
image.png
可以看到实际占用仅是20 但可被释放的空间却很大,因为引用了一个完整的字符串
但是令我疑惑的是我这样仿照这答者的代码写了一个差不多的,并没有产生内存泄漏现象,会有slicestring,但仅有一个,所以我很疑惑答主的改法是啥?我们代码之间的差别又是啥?希望能解惑

function createText () {
    return Array.from({length: 2000}).fill('dddddddddddddddd1').join('');;
}

function test (text) {
    const index = text.indexOf('1'),
            lastIndex = text.indexOf('1', index + 1),
        c = text.substring(index, lastIndex).match(/dddd(dd)ddd/) || [];
    
    return c[1].trim();
}

window.a = [];

    for (var i = 1; i < 20000; i++) {
        a.push({
            e: test(createText())
        });
    }
旧时浪漫 2022-09-19 03:14:05

JS 中字符串不可变。

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文