为什么 node.js 需要比 c# 多得多的内存?
Why does node.js need significantly more ram than c#?
今天我只是为了好玩测试了与 Node.js 相比,在 C# 命令行应用程序中相同的代码需要多少内存。
这是我在 Node.js:
中的代码
"use strict";
class User{
constructor(firstname, lastname){
this.firstName = firstname;
this.lastName = lastname;
}
}
const count = 10000000;
const users = [];
console.time('Allocating Elements');
for (let i = 0; i < count; i++) {
users.push(new User(`John${i}`,`Doe${i}`));
}
console.timeEnd('Allocating Elements');
console.log('Finished');
这是我在 C# 中的代码:
class User
{
public string firstName { get; set; }
public string lastName { get; set; }
public User(string firstname, string lastname)
{
this.firstName = firstName;
this.lastName = lastName;
}
}
class Program
{
static void Main(string[] args)
{
var count = 10000000;
var users = new List<User>();
var sw = Stopwatch.StartNew();
for (var i = 0; i < count; i++)
{
users.Add(new User($"John{i}", $"Doe{i}"));
}
sw.Stop();
Console.WriteLine($"Allocating Elements: {sw.ElapsedMilliseconds}");
Console.WriteLine("Finished");
}
}
两个程序都以 64 位执行。
当我在 C# 中使用类型对象列表时,内存消耗大致相同。
所以我的问题是:如果我在 C# 中使用用户 class,为什么 Node 需要大约 4 倍的内存?这样的优化怎么可能?
我附上了两个进程最后停在断点时的 ram 使用情况的屏幕截图。
Screenshot
我无法对 C# 发表评论,但我可以告诉您 V8 在您的示例中消耗了多少内存。对于每个 User
个对象,它需要:
"User" 对象本身:
- "hidden class"指针(对象布局说明)
- "properties" 指针(指向空后备存储)
- "elements" 指针(指向空后备存储)
- "firstName" in-object 属性 (到 firstNameString,见下文)
- "lastName" in-object 属性(到 lastNameString,见下文)
firstNameString, lastNameString:
- "hidden class"指针(将此对象标识为字符串)
- 字符串哈希
- 字符串长度
- 字符串字符:"John1234"
- 字符串字符:“567_____”
(字符串对象四舍五入到指针大小对齐。前 10,000 个 "John" 字符串和前 100,000 个 "Doe" 字符串将仅包含四个单词,但与 20,000,000 个字符串相比,您总的来说,这是一个可以忽略不计的影响。)
最后:
users
数组中的一个槽
当你全部加起来时,你有 5 + 2*5 + 1 = 16 个指针,每个指针 8 个字节,或者 1000 万用户有 1220MB。
当我 运行 你在 d8 中的代码(V8 的开发者 shell)并最终强制 GC 清理剩余的垃圾时,我看到消耗了 1258MB。差异解释为:
- 一般环境开销(JavaScript 全局变量,某些在启动时设置的引擎内部)
- 一定数量的 over-allocation 数组后备存储(正如 Joel Coehoorn 已经指出的那样)
- 在 node.js 中,我的猜测是配置了更大的最大堆大小,因此 GC 尚未启动。一旦有内存压力或空闲时间,它应该清理更多。
抱歉这么久才写。但我好像忽略了什么。很尴尬,但我真的没有首先看到它。事实上,这两个程序在执行期间确实需要几乎相同数量的 RAM。我的失败是 C# 代码中 User 对象的构造函数。
public User(string firstname, string lastname)
{
this.firstName = firstName;
this.lastName = lastName;
}
我将 "firstName" 和 "lastName" 变量分配给了它自己,而不是传递给构造函数的参数。因为这个错误 "firstName" 和 "lastName" 在 运行 期间是空的。这当然导致所需的 RAM 数量减少。
抱歉各位。但感谢您的回答。真不敢相信我没看到。
今天我只是为了好玩测试了与 Node.js 相比,在 C# 命令行应用程序中相同的代码需要多少内存。 这是我在 Node.js:
中的代码"use strict";
class User{
constructor(firstname, lastname){
this.firstName = firstname;
this.lastName = lastname;
}
}
const count = 10000000;
const users = [];
console.time('Allocating Elements');
for (let i = 0; i < count; i++) {
users.push(new User(`John${i}`,`Doe${i}`));
}
console.timeEnd('Allocating Elements');
console.log('Finished');
这是我在 C# 中的代码:
class User
{
public string firstName { get; set; }
public string lastName { get; set; }
public User(string firstname, string lastname)
{
this.firstName = firstName;
this.lastName = lastName;
}
}
class Program
{
static void Main(string[] args)
{
var count = 10000000;
var users = new List<User>();
var sw = Stopwatch.StartNew();
for (var i = 0; i < count; i++)
{
users.Add(new User($"John{i}", $"Doe{i}"));
}
sw.Stop();
Console.WriteLine($"Allocating Elements: {sw.ElapsedMilliseconds}");
Console.WriteLine("Finished");
}
}
两个程序都以 64 位执行。
当我在 C# 中使用类型对象列表时,内存消耗大致相同。
所以我的问题是:如果我在 C# 中使用用户 class,为什么 Node 需要大约 4 倍的内存?这样的优化怎么可能?
我附上了两个进程最后停在断点时的 ram 使用情况的屏幕截图。
Screenshot
我无法对 C# 发表评论,但我可以告诉您 V8 在您的示例中消耗了多少内存。对于每个 User
个对象,它需要:
"User" 对象本身:
- "hidden class"指针(对象布局说明)
- "properties" 指针(指向空后备存储)
- "elements" 指针(指向空后备存储)
- "firstName" in-object 属性 (到 firstNameString,见下文)
- "lastName" in-object 属性(到 lastNameString,见下文)
firstNameString, lastNameString:
- "hidden class"指针(将此对象标识为字符串)
- 字符串哈希
- 字符串长度
- 字符串字符:"John1234"
- 字符串字符:“567_____”
(字符串对象四舍五入到指针大小对齐。前 10,000 个 "John" 字符串和前 100,000 个 "Doe" 字符串将仅包含四个单词,但与 20,000,000 个字符串相比,您总的来说,这是一个可以忽略不计的影响。)
最后:
users
数组中的一个槽
当你全部加起来时,你有 5 + 2*5 + 1 = 16 个指针,每个指针 8 个字节,或者 1000 万用户有 1220MB。 当我 运行 你在 d8 中的代码(V8 的开发者 shell)并最终强制 GC 清理剩余的垃圾时,我看到消耗了 1258MB。差异解释为:
- 一般环境开销(JavaScript 全局变量,某些在启动时设置的引擎内部)
- 一定数量的 over-allocation 数组后备存储(正如 Joel Coehoorn 已经指出的那样)
- 在 node.js 中,我的猜测是配置了更大的最大堆大小,因此 GC 尚未启动。一旦有内存压力或空闲时间,它应该清理更多。
抱歉这么久才写。但我好像忽略了什么。很尴尬,但我真的没有首先看到它。事实上,这两个程序在执行期间确实需要几乎相同数量的 RAM。我的失败是 C# 代码中 User 对象的构造函数。
public User(string firstname, string lastname)
{
this.firstName = firstName;
this.lastName = lastName;
}
我将 "firstName" 和 "lastName" 变量分配给了它自己,而不是传递给构造函数的参数。因为这个错误 "firstName" 和 "lastName" 在 运行 期间是空的。这当然导致所需的 RAM 数量减少。
抱歉各位。但感谢您的回答。真不敢相信我没看到。