THREE js如何正确地将对象从场景中移除(仍然保留在HEAP中)

17

如何正确从场景中移除网格?在这个例子中:

    removable_items = [];
    box = new THREE.Object3D();
    scene.add(box);

    function add() {
        var mesh = new THREE.Mesh( new THREE.IcosahedronGeometry( 10, 5 ), new THREE.MeshPhongMaterial( {color: 0xFFFFFF}) );   
        box.add( mesh );
        removable_items.push(mesh);
        //clean(); ///// when is integrated in function memory is cleaned properly
    }   

    function clean() {
          if( removable_items.length > 0 ) {
            removable_items.forEach(function(v,i) {
               v.parent.remove(v);
            });
            removable_items = null;
            removable_items = [];
          }
    }

    function makeExperiment(r) {
      var i = 0;
      while (i < r) {
        add();
        i++;
        if( r === i ) console.log(r+' finnished ');
      }
    }

makeExperiment(50);

/// 在那之后,我手动设置了 clean();

场景中的网格不再可见,正如预期的那样,但仍在使用内存,一段时间后会导致内存泄漏和浏览器崩溃。

问题出在哪里?THREE.js 是否制造了其他引用?

THREE.js R73

编辑:当 clean(); 在函数中集成(现在在代码中被注释掉)时,内存会被正确清理。但是当我在执行完 makeExperiment(); 后手动设置 clean(); 时,内存不会被释放。


我正在看这个 - 你用什么程序/工具来查看内存? - dcromley
你的语句是:new THREE.IcosahedronGeometry(1, 5)第二个参数是细节。将其设置为5会导致大量处理。这是否是您想要回答的问题的一部分?默认值为1。 - dcromley
我使用高细节网格来清晰地说明内存示例。JSON或OBJ模型,或盒子几何体也存在同样的问题,只需要重复更长时间地调用makeExperiment函数。我正在使用Chrome控制台和Chrome内存快照。 - Martin
你确定 v.parent.remove(v); 能按预期工作吗? - 2pha
当我执行box.remove(v)时,yap,没有任何区别。 - Martin
1个回答

20

我做了一些实验,认为你的代码并没有什么大问题。但有一点是我学到的,就是垃圾回收器可能不会在你预期的时候运行。为了以防万一,我将你的代码包装在一个IIFE中(这是一种好习惯,但在这种情况下并非必需),并希望堆在函数完成运行并离开作用域后立即被清除。但事实上,它需要一些时间才能清除:

clean 50

所以我想,好吧,这不太好,如果在垃圾回收器闲置的那段时间内我创建了更多的对象,会怎样呢?于是我做了这个实验:

.
.
makeExperiment(50);
clean();
makeExperiment(50);
clean();
makeExperiment(50);
clean();
makeExperiment(50);
clean();
makeExperiment(50);
clean();
makeExperiment(50);
clean();
makeExperiment(50);
clean();
makeExperiment(50);
clean();

以下是发生的情况:

clean 400

垃圾回收器似乎正在执行其工作,并且您正确地删除了它们以实现此目的。然而,您可能也正在使用THREE.js渲染器,如果我理解正确,渲染器会保留对材质、几何体和纹理的引用。因此,如果这些对象未正确释放,它们将不会被垃圾收集。 THREE.js为GeometryMaterialTexture提供了一个名为.dispose()的方法,该方法将通知渲染器将其删除。因此,这就是我如何更改您的clean()函数:

removable_items.forEach(function(v,i) {
  v.material.dispose();
  v.geometry.dispose();
  box.remove(v);
});

嗨,谢谢!根据您的图表显示,在执行删除操作后,仍然会在堆中保留数据。如果我进行这个实验数千次,它会导致浏览器无响应。我正在游戏中使用此情况,并且需要重新加载新对象(敌人)和删除旧对象(死亡)。您是否尝试在add()函数中集成clean()函数?(在我的代码中是被注释掉的那一行)或者您是通过一段时间间隔手动运行clean()函数的呢? - Martin
1
@Martin 我尝试在add()函数内部和手动清理makeExperiment()之后进行清理(如上所示)。您无法精确控制浏览器垃圾回收器何时决定清除,如果要清除某个变量,您只能删除所有引用。当在add()内部调用clean()时,垃圾回收器仍然没有清理堆50次,只有大约6次,因此行为相同。浏览器无响应的原因是因为JavaScript是单线程的,并且您正在使用该线程执行1000次操作。尝试将其分成多个帧。 - micnil

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接