IDBRequest objects 在内存中保存在哪里?
Where are IDBRequest objects held in memory?
这似乎是一个奇怪的问题,但我正在尝试了解我的代码究竟在内存方面做了什么,以使其尽可能高效。
当数据库请求如req = objectStore.getAll( keyRange );
返回IDBRequest
object,结果稍后提供给result
属性 object,object 是在哪里创建的?是不是和其他JS一样object,由GC分配和释放;并且变量 req
只是对它的引用,这样一旦引用被破坏,GC 'knows' 就无法访问 object 并释放内存?
如果在短时间内发出许多此类请求,是否有办法不为每个结果消耗额外的 RAM?
例如,我感兴趣的过程是单击按钮通过 Promise.allSettled
调用两个承诺(一个写入和一个读取),一个将当前状态写入数据库,另一个检索新数据并从中构建文档片段。如果两者都满足,片段将替换 DOM.
中的一个节点
如果用户快速点击此数据,每个 read/getAll
returns 一个 IDBRequest
结果是 object 的数组,并且看起来消耗越来越多的 RAM,直到 GC 运行s。我可以观察到内存最终被释放,但想知道是否有办法避免这种情况发生。因为我知道那些 object 的最大大小,我可以将 IDBRequest 写入一个现有的 object 就像模板 object 并且只需要其中的一个或两个,例如一个用于当前请求,一个用于新请求,而不是不断添加新的 objects 直到 GC 释放那些被认为无法访问的?
感谢您考虑我的问题。
感谢您回答有关 IDBRequest object 分配位置的问题以及有关避免内存泄漏的建议。只是为了进一步解释我观察到的内容并想知道是否可行,我添加了这条注释。
我的代码中没有声明一个全局变量,所有变量都存在于函数中或者是函数的属性object;并且我在每个函数结束时将 tem 设置为 null 以防万一我错过了一些 scope/closure 隐藏的引用。在最终让 indexedDB 中的大部分 I/O 工作之后,我开始考虑当用户在我的应用程序中工作一两个小时时会发生什么。从长远来看,内存使用量是否会持续增加,即使我在所有构建和测试过程中都没有发现任何问题?
我用500个数据包填充了数据库,这意味着需要多个DB object 来构建一个新的DOM节点;每个节点从 15 到 60 objects 不等,具体取决于用户构建的内容。因此,我将 500 个数据包中的每一个都由 60 个 object 组成,并使这些 object 的大小对于测试而言过大,远远大于适当使用该工具时的预期。
然后通过一个setInterval,save-state, get-and-build promises从packet 1到500每500ms调用一次;我只观察了 maro 级别的数据使用情况。结果似乎是在任何时候,在 GC 运行 之间的 RAM 中可能有大约一百个这样的数据包。随着数据包的检索、节点的构建和替换,在从数据包 1 到 500 的遍历过程中,RAM 使用量稳步上升和下降了大约五次。下降前每次增加的最大级别略高于前一次。完成后大约 45 秒左右,内存 returns 恢复到 setInterval 开始时的位置。
因此,幸运的是,我认为没有内存泄漏。但是,RAM 的使用与 article 中关于使用 object 池的描述非常相似。我对 "Reduce Memory Churn, Reduce Garbage Collection taxes" 标题下的图表很感兴趣——那个 saw-tooth 模式消耗的内存比任何时候都需要的多得多,当它可能像第二个更小的图表时,级别,并且总共需要更少的 GC 运行s。
这个 SO question 的第一个答案几乎在最后写道,这导致 GC 也必须跟踪更多 object。
我不确定 GC 是否会 运行 以较低的总 RAM 消耗量或等到达到某个最大级别。当然,我可以在我的机器上进行测试,但总体而言这并不是很确定。
我不是在构建游戏,GC 暂停 运行 不是问题;并且用户永远不应该在 250 秒内点击 500 个数据包,并且永远不会有 500 个数据包的大小如此荒谬。也许,那个测试是不现实的;但 objective 是试图加剧长时间使用该工具的影响,并在整个过程中生成许多很多小的 object。即使是一个 get/put 的小编辑每次都会创建一个新的 object。这些是我以前没有考虑过的概念,只是专注于如何让 I/O 首先准确工作。
当你考虑多少objects 至少在 RAM 中停留了一小会儿,等待被垃圾收集,似乎可以合理地始终保持当前数据包,这样就不需要 get
操作编辑。只需在 RAM 中编辑 object 并仅使用 put
操作。那么所有那些 get
请求结果 object 的编辑都不会被创建。但这并不能消除 object 保存新请求的完整数据包的需要。
我知道浏览器的 GC 过程应该使所有这一切变得更容易,但这样做似乎使编码人员无法控制很多;我在其他问题中看到的关于 SO 的建议通常是不要担心,除非您遇到问题。我充其量只是一个业余爱好者,但我更愿意从一开始就了解背景和代码中发生的事情;也许我有一些固执,无论处理器和 RAM 的大小如何强大,我的小工具都应该根据需要使用尽可能少的资源,否则我就没有完成我的工作。
我不知道 object 池是否是一项好技术,但是,即使是,从 indexedDB 检索数据时似乎也不可能,因为 IDBRequest object 总是重新创建,永远不会写入现有的 object。
再次感谢您的解释。
IDBRequest 对象的结果 属性 就像任何其他对象一样将数据保存在内存中。当没有任何内容引用请求对象时,内存是可回收的。没有办法不为每个新结果消耗额外的内存。分配是内存获取。
Chrome 的政策是,在有争用之前,将内容保存在虚拟内存中不是问题。在有证据表明它会导致性能影响之前,您不应该担心过多的内存使用。大多数时候它不会。
但是,您可以在代码中查找保留对请求对象的引用的位置。如果你永远保留对它们的引用,那么这些对象将永远不会被释放并且不可回收。非常像带有事件侦听器的旧 IE 错误,一种内存泄漏形式。
避免这种行为的可靠新手证明方法是仅在函数中使用变量而不是全局变量。当函数调用完成时,每个函数调用变量通常可以在范围退出时回收,并且没有太多考虑,也没有明确的代码试图对已经为您管理的东西进行微观管理。例如,无需在每次使用变量后都将所有变量声明为 let
而不是 const
并设置 value = undefined;
或 delete value;
。因此,我会查看您的代码并查看您在何处保留对变量的引用,超出创建它们的函数的生命周期。那些是罪魁祸首。
这似乎是一个奇怪的问题,但我正在尝试了解我的代码究竟在内存方面做了什么,以使其尽可能高效。
当数据库请求如req = objectStore.getAll( keyRange );
返回IDBRequest
object,结果稍后提供给result
属性 object,object 是在哪里创建的?是不是和其他JS一样object,由GC分配和释放;并且变量 req
只是对它的引用,这样一旦引用被破坏,GC 'knows' 就无法访问 object 并释放内存?
如果在短时间内发出许多此类请求,是否有办法不为每个结果消耗额外的 RAM?
例如,我感兴趣的过程是单击按钮通过 Promise.allSettled
调用两个承诺(一个写入和一个读取),一个将当前状态写入数据库,另一个检索新数据并从中构建文档片段。如果两者都满足,片段将替换 DOM.
如果用户快速点击此数据,每个 read/getAll
returns 一个 IDBRequest
结果是 object 的数组,并且看起来消耗越来越多的 RAM,直到 GC 运行s。我可以观察到内存最终被释放,但想知道是否有办法避免这种情况发生。因为我知道那些 object 的最大大小,我可以将 IDBRequest 写入一个现有的 object 就像模板 object 并且只需要其中的一个或两个,例如一个用于当前请求,一个用于新请求,而不是不断添加新的 objects 直到 GC 释放那些被认为无法访问的?
感谢您考虑我的问题。
感谢您回答有关 IDBRequest object 分配位置的问题以及有关避免内存泄漏的建议。只是为了进一步解释我观察到的内容并想知道是否可行,我添加了这条注释。
我的代码中没有声明一个全局变量,所有变量都存在于函数中或者是函数的属性object;并且我在每个函数结束时将 tem 设置为 null 以防万一我错过了一些 scope/closure 隐藏的引用。在最终让 indexedDB 中的大部分 I/O 工作之后,我开始考虑当用户在我的应用程序中工作一两个小时时会发生什么。从长远来看,内存使用量是否会持续增加,即使我在所有构建和测试过程中都没有发现任何问题?
我用500个数据包填充了数据库,这意味着需要多个DB object 来构建一个新的DOM节点;每个节点从 15 到 60 objects 不等,具体取决于用户构建的内容。因此,我将 500 个数据包中的每一个都由 60 个 object 组成,并使这些 object 的大小对于测试而言过大,远远大于适当使用该工具时的预期。
然后通过一个setInterval,save-state, get-and-build promises从packet 1到500每500ms调用一次;我只观察了 maro 级别的数据使用情况。结果似乎是在任何时候,在 GC 运行 之间的 RAM 中可能有大约一百个这样的数据包。随着数据包的检索、节点的构建和替换,在从数据包 1 到 500 的遍历过程中,RAM 使用量稳步上升和下降了大约五次。下降前每次增加的最大级别略高于前一次。完成后大约 45 秒左右,内存 returns 恢复到 setInterval 开始时的位置。
因此,幸运的是,我认为没有内存泄漏。但是,RAM 的使用与 article 中关于使用 object 池的描述非常相似。我对 "Reduce Memory Churn, Reduce Garbage Collection taxes" 标题下的图表很感兴趣——那个 saw-tooth 模式消耗的内存比任何时候都需要的多得多,当它可能像第二个更小的图表时,级别,并且总共需要更少的 GC 运行s。
这个 SO question 的第一个答案几乎在最后写道,这导致 GC 也必须跟踪更多 object。
我不确定 GC 是否会 运行 以较低的总 RAM 消耗量或等到达到某个最大级别。当然,我可以在我的机器上进行测试,但总体而言这并不是很确定。
我不是在构建游戏,GC 暂停 运行 不是问题;并且用户永远不应该在 250 秒内点击 500 个数据包,并且永远不会有 500 个数据包的大小如此荒谬。也许,那个测试是不现实的;但 objective 是试图加剧长时间使用该工具的影响,并在整个过程中生成许多很多小的 object。即使是一个 get/put 的小编辑每次都会创建一个新的 object。这些是我以前没有考虑过的概念,只是专注于如何让 I/O 首先准确工作。
当你考虑多少objects 至少在 RAM 中停留了一小会儿,等待被垃圾收集,似乎可以合理地始终保持当前数据包,这样就不需要 get
操作编辑。只需在 RAM 中编辑 object 并仅使用 put
操作。那么所有那些 get
请求结果 object 的编辑都不会被创建。但这并不能消除 object 保存新请求的完整数据包的需要。
我知道浏览器的 GC 过程应该使所有这一切变得更容易,但这样做似乎使编码人员无法控制很多;我在其他问题中看到的关于 SO 的建议通常是不要担心,除非您遇到问题。我充其量只是一个业余爱好者,但我更愿意从一开始就了解背景和代码中发生的事情;也许我有一些固执,无论处理器和 RAM 的大小如何强大,我的小工具都应该根据需要使用尽可能少的资源,否则我就没有完成我的工作。
我不知道 object 池是否是一项好技术,但是,即使是,从 indexedDB 检索数据时似乎也不可能,因为 IDBRequest object 总是重新创建,永远不会写入现有的 object。
再次感谢您的解释。
IDBRequest 对象的结果 属性 就像任何其他对象一样将数据保存在内存中。当没有任何内容引用请求对象时,内存是可回收的。没有办法不为每个新结果消耗额外的内存。分配是内存获取。
Chrome 的政策是,在有争用之前,将内容保存在虚拟内存中不是问题。在有证据表明它会导致性能影响之前,您不应该担心过多的内存使用。大多数时候它不会。
但是,您可以在代码中查找保留对请求对象的引用的位置。如果你永远保留对它们的引用,那么这些对象将永远不会被释放并且不可回收。非常像带有事件侦听器的旧 IE 错误,一种内存泄漏形式。
避免这种行为的可靠新手证明方法是仅在函数中使用变量而不是全局变量。当函数调用完成时,每个函数调用变量通常可以在范围退出时回收,并且没有太多考虑,也没有明确的代码试图对已经为您管理的东西进行微观管理。例如,无需在每次使用变量后都将所有变量声明为 let
而不是 const
并设置 value = undefined;
或 delete value;
。因此,我会查看您的代码并查看您在何处保留对变量的引用,超出创建它们的函数的生命周期。那些是罪魁祸首。