JavaScript 中的活跃度是什么?
What is Liveness in JavaScript?
为了研究 JavaScript GC 的复杂性,我深入研究了杂草(即 ECMAScript 规范)。我发现一个对象只要被认为是“活的”就不应该被收集。而liveness本身定义如下:
At any point during evaluation, a set of objects S is considered
live if either of the following conditions is met:
- Any element in S is included in any agent's
[[KeptAlive]]
List.
- There exists a valid future hypothetical WeakRef-oblivious execution with respect to S that observes the Object value of any
object in S.
一旦创建了一个特殊的WeakRef
,[[KeptAlive]]
列表就会附加一个对象,它(弱)引用它,并在当前同步作业停止后清空。
但是,至于 WeakRef-oblivious execution,我想不明白它是什么:
For some set of objects S, a hypothetical WeakRef-oblivious
execution with respect to S is an execution whereby the abstract
operation WeakRefDeref of a WeakRef whose referent is an element
of S always returns undefined.
WeakRefDeref
of a WeakRef
returns undefined
当其所指对象已被收集时。我说得对吗,这里暗示应该收集构成 S
的所有对象?所以 future 假设的 WeakRef-oblivious 执行的概念是仍然有一个对象,S
的一个元素,它还没有被一些 WeakRef
收集和观察].
这一切对我来说仍然毫无意义。我会很感激一些样品。
让我们忽略形式化但不完整的定义。我们在that section.1
的非规范注释中找到实际含义
What is Liveness in JavaScript?
Liveness 是保证哪个 WeakRef
s 引擎不能为空的下限(注释 6)。因此,活动(组)对象是那些不能被垃圾收集的对象,因为它们仍将被程序使用。
但是,一组对象的存活性并不意味着所有集合中的对象必须被保留。这意味着集合中有 一些 对象仍将被程序使用,并且活动集合(作为一个整体)不能被垃圾收集。这是因为定义在 garbage collector Execution algorithm2 中以否定形式使用: 在任何时候,如果一组对象 S
是未生效,ECMAScript 实现可能3 […] 原子地[删除它们]。换句话说,如果实现选择一个非活动集S
来清空 WeakRefs,它必须同时清空 S
中所有对象的 WeakRefs(注意2).
查看单个对象,如果至少有一个包含它们的非活动集合,我们可以说它们不是活动的(垃圾可收集);反之,我们说一个单独的对象是活的,如果包含它的每组对象都是活的(注 3)。这有点奇怪,因为“活动对象集”基本上被定义为“其中任何一个活动的一组对象”,但是个体活动总是“相对于集合S
”,即这些对象是否可以一起被垃圾回收。
1:这绝对是整个规范中注释内容比最高的部分。
2:强调我的
3:来自objectives的第一段:“本规范不保证任何对象都会被垃圾收集。不活动的对象可能会在很长时间后被释放一段时间,或者根本不会。因此,本规范在描述由垃圾收集触发的行为时使用术语“可能”。
现在,让我们试着理解这个定义。
At any point during evaluation, a set of objects S
is considered
live if either of the following conditions is met:
- Any element in
S
is included in any agent's [[KeptAlive]]
List.
- There exists a valid future hypothetical WeakRef-oblivious execution
with respect to
S
that observes the Object value of any object in S
.
第一个条件很清楚。 agent is representing the list of objects to be kept alive until the end of the current Job. It is cleared after a synchronous run of execution ends, and the note on WeakRef.prototype.deref
4 provides further insight on the intention: If [WeakRefDeref] return 的 [[KeptAlive]]
列表不是 undefined
的 target
对象,那么这个 target
对象不应该被垃圾回收,直到当前 ECMAScript 代码的执行已经完成。
不过第二个条件哦。没有明确定义“有效”、“未来执行”和“观察对象值”的含义。 上面的第二个条件旨在捕捉的直觉是,如果一个对象的身份可以通过非 WeakRef 方式观察到,那么该对象就是活的(注 2),啊哈。根据我的理解,“执行”是代理执行 JavaScript 代码以及在此期间发生的操作。如果它符合 ECMAScript 规范,它就是“有效的”。如果从程序的当前状态开始,它就是“未来”。
可以通过观察对象之间的严格相等比较或观察对象在 Map 中用作键来观察对象的身份(注释 4),据此我假设该注释仅提供示例和“对象值”的意思是“身份”。似乎重要的是代码是否关心或不关心是否使用了特定对象,并且所有这些仅在执行结果为 observable 时(即如果不更改 [=194= 就无法优化) ] 的程序)5.
要通过这些方法确定对象的活跃度,需要测试所有可能的未来执行,直到对象不再可观察为止。因此,这里定义的活跃度是 undecidable6. In practice, engines use conservative approximations such as reachability7 (note 6), but notice that research on more advanced garbage-collectors 正在进行中。
现在有趣的是:是什么让执行“相对于一组对象 S
假设的 WeakRef-oblivious”?这意味着在 S
中对象的所有 WeakRefs 都已被清除 8 的假设下执行。我们假设在未来的执行过程中,抽象操作WeakRefDeref 的 WeakRef
的引用对象是 S
的元素总是 returns undefined
(def),然后返回它是否仍然可以观察集合中的元素。 如果弱引用清除后none个待观察对象可以被垃圾回收否则S
被认为是存活的, 对象不能被垃圾回收并且不能清除对它们的弱引用。
4: 例子见全文。有趣的是,new WeakRef(obj)
构造函数也将 obj
添加到 [[KeptAlive]]
列表中。
5:不幸的是,根据 this very interesting es-discourse thread.[=137,“什么构成“观察”的概念故意含糊不清” =]
6: 虽然指定不可判定的属性看起来毫无用处,但实际上并非如此。指定一个更差的近似值,例如所说的可达性,将排除 一些 在实践中可能的优化,即使不可能实现通用的 100% 优化器。 dead code elimination.
的情况类似
7: 指定可达性的概念实际上比描述活跃性要复杂得多。请参阅注释 5,它给出了一些结构示例,其中对象可以通过内部槽和规范类型字段访问但应该被垃圾收集 nonetheless.
8:另见 issue 179 in the proposal and the corresponding PR 了解为什么引入对象集。
示例时间!
It is hard to me to recognize how livenesses of several objects may affect each other.
WeakRef-obliviousness,连同活跃度,捕捉[概念]WeakRef 本身不会让对象保持活动状态(注释 1)。这几乎就是 WeakRef 的目的,但无论如何让我们看一个例子:
{
const o = {};
const w = new WeakRef(o);
t = setInterval(() => {
console.log(`Weak reference was ${w.deref() ? "kept" : "cleared"}.`)
}, 1000);
}
(您可以在控制台中 运行,然后 force garbage collection,然后 clearInterval(t);
)
[第二个概念是]活跃度的循环并不意味着对象是活跃的(注 1)。这个有点难展示,但是看这个例子:
{
const o = {};
const w = new WeakRef(o);
setTimeout(() => {
console.log(w.deref() && w.deref() === o ? "kept" : "cleared")
}, 1000);
}
在这里,我们清楚地观察到了o
的身份。所以它一定是活的?仅当保存 o
的 w
未被清除时,否则 … === o
不会被评估。所以(包含的集合)o
的活跃度取决于它自己,通过循环推理,一个聪明的垃圾收集器实际上允许收集它而不管闭包。
具体来说,如果确定obj's
活跃度取决于确定另一个WeakRef引用对象的活跃度,obj2
,obj2
的活跃度不能假设obj's
liveness,这就是循环推理(注1)。让我们尝试用两个相互依赖的对象做一个例子:
{
const a = {}, b = {};
const wa = new WeakRef(a), wb = new WeakRef(b);
const lookup = new WeakMap([[a, "b kept"], [b, "a kept"]]);
setTimeout(() => {
console.log(wa.deref() ? lookup.get(b) : "a cleared");
console.log(wb.deref() ? lookup.get(a) : "b cleared");
}, 1000);
}
WeakMap
主要用作观察两个对象的身份的东西。在这里,如果 a
被保留,那么 wa.deref()
就会 return 它,b
被观察到;如果 b
被保留,那么 wb.deref()
将 return 它, a
被观察到。他们的活跃度是相互依赖的,但是我们一定不能做循环推理。垃圾收集器可以同时清除 wa
和 wb
,但不能只清除其中一个。
Chrome 目前确实通过闭包检查可达性,因此上面的代码片段不起作用,但我们可以通过在对象之间引入循环依赖来删除这些引用:
{
const a = {}, b = {};
a.b = b; b.a = a;
const wa = new WeakRef(a), wb = new WeakRef(b);
const lookup = new WeakMap([[a, "b kept"], [b, "a kept"]]);
t = setInterval(() => {
console.log(wa.deref() ? lookup.get(wa.deref().b) : "a cleared");
console.log(wb.deref() ? lookup.get(wb.deref().a) : "b cleared");
}, 1000);
}
对我来说,注释 2(WeakRef-obliviousness 是在对象集而不是单个对象上定义的,以解释循环。如果它是在单个对象上定义的,那么循环中的对象将是即使它的 Object 值仅通过循环中其他对象的 WeakRefs 观察到,也被认为是实时的。) 似乎说的是完全相同的事情。注释被介绍给 fix the definition of liveness to handle cycles,那个问题还包括一些有趣的例子。
为了研究 JavaScript GC 的复杂性,我深入研究了杂草(即 ECMAScript 规范)。我发现一个对象只要被认为是“活的”就不应该被收集。而liveness本身定义如下:
At any point during evaluation, a set of objects S is considered live if either of the following conditions is met:
- Any element in S is included in any agent's
[[KeptAlive]]
List.- There exists a valid future hypothetical WeakRef-oblivious execution with respect to S that observes the Object value of any object in S.
一旦创建了一个特殊的WeakRef
,[[KeptAlive]]
列表就会附加一个对象,它(弱)引用它,并在当前同步作业停止后清空。
但是,至于 WeakRef-oblivious execution,我想不明白它是什么:
For some set of objects S, a hypothetical WeakRef-oblivious execution with respect to S is an execution whereby the abstract operation WeakRefDeref of a WeakRef whose referent is an element of S always returns undefined.
WeakRefDeref
of a WeakRef
returns undefined
当其所指对象已被收集时。我说得对吗,这里暗示应该收集构成 S
的所有对象?所以 future 假设的 WeakRef-oblivious 执行的概念是仍然有一个对象,S
的一个元素,它还没有被一些 WeakRef
收集和观察].
这一切对我来说仍然毫无意义。我会很感激一些样品。
让我们忽略形式化但不完整的定义。我们在that section.1
的非规范注释中找到实际含义What is Liveness in JavaScript?
Liveness 是保证哪个 WeakRef
s 引擎不能为空的下限(注释 6)。因此,活动(组)对象是那些不能被垃圾收集的对象,因为它们仍将被程序使用。
但是,一组对象的存活性并不意味着所有集合中的对象必须被保留。这意味着集合中有 一些 对象仍将被程序使用,并且活动集合(作为一个整体)不能被垃圾收集。这是因为定义在 garbage collector Execution algorithm2 中以否定形式使用: 在任何时候,如果一组对象 S
是未生效,ECMAScript 实现可能3 […] 原子地[删除它们]。换句话说,如果实现选择一个非活动集S
来清空 WeakRefs,它必须同时清空 S
中所有对象的 WeakRefs(注意2).
查看单个对象,如果至少有一个包含它们的非活动集合,我们可以说它们不是活动的(垃圾可收集);反之,我们说一个单独的对象是活的,如果包含它的每组对象都是活的(注 3)。这有点奇怪,因为“活动对象集”基本上被定义为“其中任何一个活动的一组对象”,但是个体活动总是“相对于集合S
”,即这些对象是否可以一起被垃圾回收。
1:这绝对是整个规范中注释内容比最高的部分。
2:强调我的
3:来自objectives的第一段:“本规范不保证任何对象都会被垃圾收集。不活动的对象可能会在很长时间后被释放一段时间,或者根本不会。因此,本规范在描述由垃圾收集触发的行为时使用术语“可能”。
现在,让我们试着理解这个定义。
At any point during evaluation, a set of objects
S
is considered live if either of the following conditions is met:
- Any element in
S
is included in any agent's[[KeptAlive]]
List.- There exists a valid future hypothetical WeakRef-oblivious execution with respect to
S
that observes the Object value of any object inS
.
第一个条件很清楚。 agent is representing the list of objects to be kept alive until the end of the current Job. It is cleared after a synchronous run of execution ends, and the note on WeakRef.prototype.deref
4 provides further insight on the intention: If [WeakRefDeref] return 的 [[KeptAlive]]
列表不是 undefined
的 target
对象,那么这个 target
对象不应该被垃圾回收,直到当前 ECMAScript 代码的执行已经完成。
不过第二个条件哦。没有明确定义“有效”、“未来执行”和“观察对象值”的含义。 上面的第二个条件旨在捕捉的直觉是,如果一个对象的身份可以通过非 WeakRef 方式观察到,那么该对象就是活的(注 2),啊哈。根据我的理解,“执行”是代理执行 JavaScript 代码以及在此期间发生的操作。如果它符合 ECMAScript 规范,它就是“有效的”。如果从程序的当前状态开始,它就是“未来”。
可以通过观察对象之间的严格相等比较或观察对象在 Map 中用作键来观察对象的身份(注释 4),据此我假设该注释仅提供示例和“对象值”的意思是“身份”。似乎重要的是代码是否关心或不关心是否使用了特定对象,并且所有这些仅在执行结果为 observable 时(即如果不更改 [=194= 就无法优化) ] 的程序)5.
要通过这些方法确定对象的活跃度,需要测试所有可能的未来执行,直到对象不再可观察为止。因此,这里定义的活跃度是 undecidable6. In practice, engines use conservative approximations such as reachability7 (note 6), but notice that research on more advanced garbage-collectors 正在进行中。
现在有趣的是:是什么让执行“相对于一组对象 S
假设的 WeakRef-oblivious”?这意味着在 S
中对象的所有 WeakRefs 都已被清除 8 的假设下执行。我们假设在未来的执行过程中,抽象操作WeakRefDeref 的 WeakRef
的引用对象是 S
的元素总是 returns undefined
(def),然后返回它是否仍然可以观察集合中的元素。 如果弱引用清除后none个待观察对象可以被垃圾回收否则S
被认为是存活的, 对象不能被垃圾回收并且不能清除对它们的弱引用。
4: 例子见全文。有趣的是,new WeakRef(obj)
构造函数也将 obj
添加到 [[KeptAlive]]
列表中。
5:不幸的是,根据 this very interesting es-discourse thread.[=137,“什么构成“观察”的概念故意含糊不清” =]
6: 虽然指定不可判定的属性看起来毫无用处,但实际上并非如此。指定一个更差的近似值,例如所说的可达性,将排除 一些 在实践中可能的优化,即使不可能实现通用的 100% 优化器。 dead code elimination.
的情况类似
7: 指定可达性的概念实际上比描述活跃性要复杂得多。请参阅注释 5,它给出了一些结构示例,其中对象可以通过内部槽和规范类型字段访问但应该被垃圾收集 nonetheless.
8:另见 issue 179 in the proposal and the corresponding PR 了解为什么引入对象集。
示例时间!
It is hard to me to recognize how livenesses of several objects may affect each other.
WeakRef-obliviousness,连同活跃度,捕捉[概念]WeakRef 本身不会让对象保持活动状态(注释 1)。这几乎就是 WeakRef 的目的,但无论如何让我们看一个例子:
{
const o = {};
const w = new WeakRef(o);
t = setInterval(() => {
console.log(`Weak reference was ${w.deref() ? "kept" : "cleared"}.`)
}, 1000);
}
(您可以在控制台中 运行,然后 force garbage collection,然后 clearInterval(t);
)
[第二个概念是]活跃度的循环并不意味着对象是活跃的(注 1)。这个有点难展示,但是看这个例子:
{
const o = {};
const w = new WeakRef(o);
setTimeout(() => {
console.log(w.deref() && w.deref() === o ? "kept" : "cleared")
}, 1000);
}
在这里,我们清楚地观察到了o
的身份。所以它一定是活的?仅当保存 o
的 w
未被清除时,否则 … === o
不会被评估。所以(包含的集合)o
的活跃度取决于它自己,通过循环推理,一个聪明的垃圾收集器实际上允许收集它而不管闭包。
具体来说,如果确定obj's
活跃度取决于确定另一个WeakRef引用对象的活跃度,obj2
,obj2
的活跃度不能假设obj's
liveness,这就是循环推理(注1)。让我们尝试用两个相互依赖的对象做一个例子:
{
const a = {}, b = {};
const wa = new WeakRef(a), wb = new WeakRef(b);
const lookup = new WeakMap([[a, "b kept"], [b, "a kept"]]);
setTimeout(() => {
console.log(wa.deref() ? lookup.get(b) : "a cleared");
console.log(wb.deref() ? lookup.get(a) : "b cleared");
}, 1000);
}
WeakMap
主要用作观察两个对象的身份的东西。在这里,如果 a
被保留,那么 wa.deref()
就会 return 它,b
被观察到;如果 b
被保留,那么 wb.deref()
将 return 它, a
被观察到。他们的活跃度是相互依赖的,但是我们一定不能做循环推理。垃圾收集器可以同时清除 wa
和 wb
,但不能只清除其中一个。
Chrome 目前确实通过闭包检查可达性,因此上面的代码片段不起作用,但我们可以通过在对象之间引入循环依赖来删除这些引用:
{
const a = {}, b = {};
a.b = b; b.a = a;
const wa = new WeakRef(a), wb = new WeakRef(b);
const lookup = new WeakMap([[a, "b kept"], [b, "a kept"]]);
t = setInterval(() => {
console.log(wa.deref() ? lookup.get(wa.deref().b) : "a cleared");
console.log(wb.deref() ? lookup.get(wb.deref().a) : "b cleared");
}, 1000);
}
对我来说,注释 2(WeakRef-obliviousness 是在对象集而不是单个对象上定义的,以解释循环。如果它是在单个对象上定义的,那么循环中的对象将是即使它的 Object 值仅通过循环中其他对象的 WeakRefs 观察到,也被认为是实时的。) 似乎说的是完全相同的事情。注释被介绍给 fix the definition of liveness to handle cycles,那个问题还包括一些有趣的例子。