首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >垃圾收集时NodeJS ref-struct中损坏的内容

垃圾收集时NodeJS ref-struct中损坏的内容
EN

Stack Overflow用户
提问于 2018-08-10 19:48:15
回答 1查看 281关注 0票数 4

嵌套在另一个ref-struct实例中,嵌套对象的一个属性在手动垃圾收集中损坏。

请参阅此最小代码再现:https://github.com/hunterlester/minimum-ref-struct-corruption

请注意,在日志输出的第3行中,name的值没有损坏:

代码语言:javascript
复制
Running garbage collection...
authGranted object afte gc:  { name: '�_n9a\u0002', 'ref.buffer': <Buffer@0x00000261396F3910 18 86 6c 39 61 02 00 00> }
Unnested access container entry after gc:  { name: 'apps/net.maidsafe.examples.mailtutorial', 'ref.buffer': <Buffer@0x00000261396F3B10 60 68 6e 39 61 02 00 00> }
Globally assigned values after gc:  apps/net.maidsafe.examples.mailtutorial  _publicNames
EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2018-08-14 07:04:59

虽然refref-structref-array都很强大,但却很脆弱,但它们的组合可能表现得很模糊。

你的样本有两个细微之处:

  1. 调用makeAccessContainerEntry两次覆盖全局缓存-- CStrings 缓存 (global.x0global.x1)在makeAuthGrantedFfiStruct调用期间将被第二个直接打电话覆盖。
  2. 似乎您也应该缓存每个ContainerInfoArray

这段代码应该可以正常工作:

代码语言:javascript
复制
const ArrayType = require('ref-array');
const ref = require('ref');
const Struct = require('ref-struct');
const CString = ref.types.CString;

const ContainerInfo = Struct({
  name: CString
});

const ContainerInfoArray = new ArrayType(ContainerInfo);

const AccessContainerEntry = Struct({
  containers: ref.refType(ContainerInfo)
});

const AuthGranted = Struct({
  access_container_entry: AccessContainerEntry
});

const accessContainerEntry = [
  {
    "name": "apps/net.maidsafe.examples.mailtutorial",
  },
  {
    "name": "_publicNames",
  }
];

const makeAccessContainerEntry = (accessContainerEntry) => {
  const accessContainerEntryCache = {
    containerInfoArrayCache: null,
    containerInfoCaches: [],
  };
  accessContainerEntryCache.containerInfoArrayCache = new ContainerInfoArray(accessContainerEntry.map((entry, index) => {
    const name = ref.allocCString(entry.name);
    accessContainerEntryCache.containerInfoCaches.push(name);
    return new ContainerInfo({ name });
  }));
  return {
    accessContainerEntry: new AccessContainerEntry({
      containers: accessContainerEntryCache.containerInfoArrayCache.buffer,
    }),
    accessContainerEntryCache,
  };
};

const makeAuthGrantedFfiStruct = () => {
  const ace = makeAccessContainerEntry(accessContainerEntry);
  return {
    authGranted: new AuthGranted({
      access_container_entry: ace.accessContainerEntry,
    }),
    authGrantedCache: ace.accessContainerEntryCache,
  };
}

const authGranted = makeAuthGrantedFfiStruct();
const unNestedContainerEntry = makeAccessContainerEntry(accessContainerEntry);

if(global.gc) {
  console.log('Running garbage collection...');
  global.gc();
}

console.log('authGranted object afte gc: ', authGranted.authGranted.access_container_entry.containers.deref());
console.log('Unnested access container entry after gc: ', unNestedContainerEntry.accessContainerEntry.containers.deref());

如您所见,我将缓存添加到makeAccessContainerEntry输出中,只要您需要从垃圾收集中保存数据,就应该将其保存在某个地方。

编辑:一些背景

JS实现了高级内存管理,当没有对特定对象的引用时,引用引用对象并释放内存。

在C中没有引用和GC,但是有指针,它们只是指向特定结构或内存块所在位置的内存地址。

ref使用以下技术绑定这两种数据:C指针是一个缓冲区,用于存储实际数据位于内存中的内存地址。实际数据通常也表示为缓冲区。

ref-structref的一个附件,它实现了将底层内存块(缓冲区)解释为结构的能力--用户定义类型以及它们在内存中的位置,ref-struct试图读取内存块的相应部分并获取值。

ref-arrayref的一个附件,它实现了将底层内存块(缓冲区)解释为数组的能力--用户定义类型以及它们在内存中的位置,ref-array试图读取内存块的相应部分并获取数组项。

这样,如果为某物分配一个缓冲区,然后获得对它的ref引用(一个只保存原始缓冲区的内存地址的新缓冲区),并丢失对原始缓冲区的JS引用,那么GC可以像这样释放原始缓冲区:

代码语言:javascript
复制
function allocateData() {
  const someData = Buffer.from('sometext');
  return ref.ref(data);
}

const refReference = allocateData();
// There are no more direct JS references to someData - they are all left in the scope of allocateData() function.

console.log(refReference.deref());
global.gc(); // As long as there are no more JS references to someData, GC will release it and use its memory for something else.
console.log(refReference.deref());

不要急于测试这段代码--两个console.log(refReference.deref());都会打印相同的输出,因为refrefReference中保存了对引用的data的隐藏引用。

ref-structref-array知道这种情况,并且通常也正确地保存对所引用数据的隐藏引用。但是ref-structref-array的组合揭示了一个bug或潜在的不兼容性,隐藏的引用有时会丢失。解决办法是自己缓存引用--这是我建议使用的方法。

票数 5
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/51793236

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档