为什么 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 数量减少。

抱歉各位。但感谢您的回答。真不敢相信我没看到。