【发布时间】: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