【问题标题】:Where are IDBRequest objects held in memory?IDBRequest 对象在内存中保存在哪里?
【发布时间】:2020-08-08 18:17:00
【问题描述】:

这似乎是一个奇怪的问题,但我试图了解我的代码在内存方面究竟做了什么,以使其尽可能高效。

当发出req = objectStore.getAll( keyRange );之类的数据库请求并返回IDBRequest对象并将结果提供给对象的result属性时,该对象在哪里创建?是不是和其他 JS 对象一样,由 GC 分配和释放?而变量req 只是对它的引用,这样一旦引用被破坏,GC 就会“知道”对象无法访问并释放内存?

如果在短时间内发出许多此类请求,是否有办法不为每个结果消耗额外的 RAM?

例如,我感兴趣的过程是单击按钮通过Promise.allSettled 调用两个承诺(一个写入和一个读取),一个将当前状态写入数据库,另一个检索新数据和从中构建一个文档片段。如果两者都满足,则片段将替换 DOM 中的一个节点。

如果用户快速单击此数据,则每个 read/getAll 都会返回一个 IDBRequest,其结果是一个对象数组,并且在 GC 运行之前似乎会消耗越来越多的 RAM。我可以观察到内存最终会被释放,但想知道是否有办法避免这种情况发生。既然我知道这些对象的最大大小是多少,我是否可以将 IDBRequest 写入一个类似于模板对象的现有对象,并且只需要其中的一两个,例如一个用于当前请求,一个用于新请求,而不是不断添加新对象,直到 GC 释放那些被认为无法访问的对象?

感谢您考虑我的问题。


感谢有关 IDBRequest 对象分配位置的回答以及有关避免内存泄漏的建议。只是为了进一步解释我观察到的情况并想知道这是否可能,我添加了这个注释。

我的代码中没有声明一个全局变量,所有变量都存在于函数中或者是函数对象的属性;我在每个函数结束时将 tem 设置为 null,以防我错过了一些作用域/闭包隐藏引用。在最终让 indexedDB 中的大部分 I/O 正常工作后,我开始考虑当用户在我的应用程序中工作一两个小时时会发生什么。即使我在所有构建和测试过程中没有发现任何问题,内存使用量是否会在长期内持续增加?

我用 500 个数据包填充了数据库,这意味着构建一个新的 DOM 节点需要多个 DB 对象;每个节点有 15 到 60 个对象,具体取决于用户构建的内容。因此,我将 500 个数据包中的每一个数据包都由 60 个对象组成,并让这些对象的尺寸过大以进行测试,远远大于在适当使用该工具期间的预期。

然后通过 setInterval,从数据包 1 到 500 每 500 毫秒调用一次保存状态、获取和构建承诺;我只观察了 maro 级别的数据使用情况。结果似乎在任何时候,在 GC 运行之间的 RAM 中可能有大约一百个这样的数据包。随着数据包的检索以及节点的构建和替换,在从数据包 1 到 500 的遍历过程中,RAM 使用量稳步增加和下降约 5 次。每次下降之前的最大水平都比前一次高一点。完成后大约 45 秒左右,内存返回到 setInterval 开始时的位置。

因此,幸运的是,我认为没有内存泄漏。然而,RAM 的使用与article 中关于使用对象池的描述非常相似。我对“减少内存流失,减少垃圾收集税”标题下的图表感兴趣 - 这种锯齿模式消耗的内存比任何时候都需要的多得多,当它可能像第二张图一样更小、更水平,并且总共需要更少的 GC 运行。

这个 SO question 的第一个答案几乎在最后写道,这导致 GC 还必须跟踪更多对象。

我不确定 GC 是否会以较低的总 RAM 消耗运行,或者会等到达到某个最高级别。当然,我可以在我的机器上进行测试,但总体而言这不是很确定。

我不是在构建游戏,GC 运行的暂停不是问题;用户不应该在总共 250 秒内点击 500 个数据包,也不会有 500 个如此荒谬大小的数据包。也许,那个测试是不现实的;但其目的是试图加剧长时间使用该工具的效果,并在整个过程中生成许多、许多小物体。即使是次要编辑的 get/put 每次都会创建一个新对象。这些是我之前没有考虑过的概念,只是专注于如何首先让 I/O 准确工作。

当您考虑至少有多少对象在 RAM 中闲置一段时间,等待被垃圾收集时,始终保持当前数据包似乎是合理的,这样就不需要 get 操作编辑。只需编辑 RAM 中的对象并仅使用 put 操作。然后永远不会创建所有那些get 请求编辑的结果对象。但这并不能消除对象保存新请求的完整数据包的需要。

我知道浏览器的 GC 过程应该使这一切变得更容易,但这样做似乎会使编码人员无法控制很多事情;我在其他问题上看到的关于 SO 的建议通常是不要担心它,除非你遇到问题。我充其量只是一个业余爱好者,但我更愿意从一开始就了解背景和代码中发生的事情;也许我有些固执,不管处理器和 RAM 的大小有多强大,我的小工具都应该使用尽可能少的资源,否则我还没有完成我的工作。

我不知道对象池是否是一种好的技术,但是,即使是,从 indexedDB 检索数据似乎也是不可能的,因为 IDBRequest 对象总是重新创建并且永远无法写入现有对象。

再次感谢您的解释。

【问题讨论】:

    标签: indexeddb


    【解决方案1】:

    IDBRequest 对象的结果属性将数据保存在内存中,就像任何其他对象一样。当没有引用请求对象时,内存是可回收的。没有办法不为每个新结果消耗额外的内存。分配是一种内存获取。

    Chrome 的政策是,在出现争用之前,将内容保存在虚拟内存中不是问题。在有证据证明它会导致性能影响之前,您不应该担心过多的内存使用。大多数时候它不会。

    但是,您可以查找在代码中保留对请求对象的引用的位置。如果您永远保留对它们的引用,那么这些对象将永远不会被释放并且不可回收。非常类似于带有事件侦听器的旧 IE 错误,一种内存泄漏形式。

    避免这种行为的万无一失的新手证明方法是只在函数中使用变量而不是全局变量。每个函数调用变量通常在作用域退出时可回收,当函数调用完成时,没有太多的想法涉及,并且没有显式代码试图对已经为您管理的东西进行微观管理。例如,无需将所有变量声明为let 而不是const,并在每次使用变量后设置value = undefined;delete value;。因此,我会查看您的代码,并查看您在创建它们的函数的生命周期之外保留对变量的引用的位置。这些才是罪魁祸首。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-07-05
      • 1970-01-01
      • 1970-01-01
      • 2011-08-28
      相关资源
      最近更新 更多