如何管理用于高规模服务的单写数据库管理系统?

10,115次阅读
没有评论

共计 3256 个字符,预计需要花费 9 分钟才能阅读完成。

最近我一直在进行一个项目,旨在创建一个实时高性能的 JavaScript 图表库。该项目使用了一个雄心勃勃且新颖的技术堆栈,其中包括一个大型的 C /C++ 遗留代码库,使用 Emscripten 编译为 WebAssembly,针对 WebGL 进行渲染,并提供 TypeScript API 包装器,可在 JavaScript 中加载图表而无需担心底层的 Wasm。

为什么要使用 WebAssembly?

WebAssembly 是一项令人兴奋的技术,在许多情况下比 JavaScript 具有更好的性能优势。而且,在这种情况下,由于遗留的 C ++ 代码库已经处理了 OpenGL 中的大部分图表渲染工作,只需要做一些工作即可将其定位到 WebGL。

使用 Emscripten 将现有的 C ++ 代码编译成 WebAssembly 非常简单,只需编写绑定生成类型并在 Wasm 库周围编写 JavaScript API 即可使用它。

在开发库的过程中,我们学到了一些关于 WebAssembly 内存模型的有趣事实,以及如何避免和调试内存泄漏,下面我将分享给大家。

JavaScript 与 WebAssembly 内存模型的区别

WebAssembly 与 JavaScript 具有完全不同的内存模型。虽然 JavaScript 拥有自动垃圾回收器,可以自动清理不再需要的变量的内存,但 WebAssembly 则没有。在 Wasm 内存中声明的对象或缓冲区必须由调用者删除,否则将导致内存泄漏。

JavaScript 中的内存泄漏是如何引起的

无论是 JavaScript 还是 WebAssembly,都可能发生内存泄漏,开发人员必须小心确保在使用 WebAssembly 时正确清理内存。

尽管 JavaScript 是一种带有垃圾回收机制的托管式编程语言,但使用纯粹的 JavaScript 仍然很容易创建内存泄漏。以下是在 JavaScript 应用程序中无意中导致内存泄漏的几种方式:

  • 箭头函数和闭包可以捕获变量并使其保持活动状态,因此无法被 JavaScript 垃圾回收器删除。

  • 回调函数或事件监听器可以捕获变量并保持其活动状态。

  • 全局变量或静态变量在应用程序的生命周期中一直存在。如果忘记使用 let 或 const 关键字,变量将转换为全局变量。

  • 即使从 DOM 中分离的节点也可以在 JavaScript 中保持对象的活动状态。仅仅移除一个节点但保留对它的引用变量,将阻止该节点及其子节点被回收。

WebAssembly 中的内存泄漏是如何引起的

Wasm 拥有与 JavaScript 虚拟机不同的堆内存。该内存在浏览器中分配,并从主机操作系统中保留。当您在 Wasm 中分配内存时,Wasm 堆会增长,并且保留了一定范围的地址。当您删除 Wasm 中的内存时,堆不会缩小,并且内存也不会返回给主机操作系统。相反,内存只是被标记为已删除或可用。这意味着它可以被未来的分配重新使用。

要在 WebAssembly 中引起内存泄漏,只需分配内存并忘记删除它即可。由于没有自动垃圾回收、终结或将内存标记为不再需要的机制,必须由用户来处理。由编译器 Emscripten 导出的所有 WebAssembly 类型都具有一种在使用 Wasm 内存的对象上调用的 `.delete()` 函数。当不再需要该对象时,需要调用此函数进行删除。以下是一个快速示例:

示例:Wasm 中的内存泄漏

假设您在 C ++ 中声明了如下类型:

// person.cpp
#include 
class Person {
public:
  // C++ 构造函数
  Person(std::string name, int age) : name(name), age(age) {}
  // C++ 析构函数
  ~Person() {}
  std::string getName() { return name;}
  int getAge() { return age;}
private:
  std::string name;
  int age;
};

` 然后使用 Emscripten 编译和导出该类型,如下所示:

emcc person.cpp -o person.js -s EXPORTED_FUNCTIONS="['_createPerson','_deletePerson','_getName','_getAge']" -s MODULARIZE=1

现在,您可以在 JavaScript 中实例化、使用和删除该类型,如下所示:

const Module = require('./person.js'); // 包含生成的 JavaScript 接口
Module.onRuntimeInitialized = () => {
  // 实例化一个 Person 对象
  const person = new Module.Person('John Doe', 30);
  console.log('创建 Person 对象:', person);
  // 访问并打印属性
  console.log('姓名:', person.getName());
  console.log('年龄:', person.getAge());
  // 删除 Person 对象(调用 C ++ 析构函数)person.delete();};

然而,如果忘记调用 `.delete()` 函数,就会导致 Wasm 内存泄漏。浏览器的内存将增长而不会缩小。

检测 WebAssembly 应用程序中的内存泄漏

由于内存泄漏对应用程序来说是灾难性的,我们不仅要确保我们的代码不会泄漏内存,还要确保用户代码(即使用我们的 JavaScript 图表库的应用程序)不会泄漏内存。

为此,我们开发了内部内存调试工具。它实现为一个对象注册表,其中包含所有未删除和未收集的对象的 Map,其中 TObjectEntryInfo 是一个存储对象的 WeakRef 的类型。

通过使用 JavaScript 代理技术,我们能够拦截对所有 WebAssembly 类型的 new/delete 的调用。每次实例化一个对象时,我们将其添加到 objectRegistry 中;每次删除一个对象时,我们将其从 objectRegistry 中移除。

现在,您可以运行应用程序,启用内存调试工具,并输出应用程序状态的特定快照。以下是该工具输出的示例。

首先,启用 MemoryUsageHelper(内存调试工具):

import {MemoryUsageHelper} from "scichart";
MemoryUsageHelper.isMemoryUsageDebugEnabled = true;

这将自动跟踪我们库中的所有类型,但您也可以通过调用 register 和 unregister 来跟踪应用程序中的任意对象:

// 注册一个任意对象
MemoryUsageHelper.register(yourObject, "identifier");
// 注销一个任意对象
MemoryUsageHelper.unregister("identifier");

稍后,在特定的点上通过调用此函数输出一个快照:

MemoryUsageHelper.objectRegistry.log();

这将在控制台输出所有未被删除或未收集的对象和它们的标识符。通过检查这个快照,您可以确定是否有任何内存泄漏。 文章来源地址 https://www.toymoban.com/diary/apps/652.html

到此这篇关于如何管理用于高规模服务的单写数据库管理系统?的文章就介绍到这了, 更多相关内容可以在右上角搜索或继续浏览下面的相关文章,希望大家以后多多支持 TOY 模板网!

原文地址:https://www.toymoban.com/diary/apps/652.html

如若转载,请注明出处:如若内容造成侵权 / 违法违规 / 事实不符,请联系站长进行投诉反馈,一经查实,立即删除!

    正文完
     0
    Yojack
    版权声明:本篇文章由 Yojack 于1970-01-01发表,共计3256字。
    转载说明:
    1 本网站名称:优杰开发笔记
    2 本站永久网址:https://yojack.cn
    3 本网站的文章部分内容可能来源于网络,仅供大家学习与参考,如有侵权,请联系站长进行删除处理。
    4 本站一切资源不代表本站立场,并不代表本站赞同其观点和对其真实性负责。
    5 本站所有内容均可转载及分享, 但请注明出处
    6 我们始终尊重原创作者的版权,所有文章在发布时,均尽可能注明出处与作者。
    7 站长邮箱:laylwenl@gmail.com
    评论(没有评论)