Javascript Three.js r88-内存泄漏

Javascript Three.js r88-内存泄漏,javascript,three.js,Javascript,Three.js,转发:我已经查看了我能找到的每一篇three.js内存泄漏帖子,其中大多数都过时了,没有推荐的函数。在这种情况下,即使是较新的方法也不会产生效果 客户端框架:React v15.5.4,ThreeJS r88 以下是JS堆的性能配置文件: 在渲染循环期间,一切正常。在我更改组件并在ComponentComponentWillUnmount生命周期方法中触发清理功能后,我希望蓝色JS堆行位于底部。不幸的是,情况并非如此。蓝线变平的地方是发生组件更改并执行清理代码的地方。不知怎的堆增加了 以下是将

转发:我已经查看了我能找到的每一篇three.js内存泄漏帖子,其中大多数都过时了,没有推荐的函数。在这种情况下,即使是较新的方法也不会产生效果

客户端框架:React v15.5.4,ThreeJS r88

以下是JS堆的性能配置文件:

在渲染循环期间,一切正常。在我更改组件并在ComponentComponentWillUnmount生命周期方法中触发清理功能后,我希望蓝色JS堆行位于底部。不幸的是,情况并非如此。蓝线变平的地方是发生组件更改并执行清理代码的地方。不知怎的堆增加了

以下是将对象添加到场景和清理/处置机制的代码:

/*
* This is how the models are added to the scene. A listener is added to each object in the scene
* to invoke Builder.prototype.model._dispose when the remove event is triggered
*/
//..
init: {
    // ...
    _modelInScene: function( modelData ) {
        const { dimensions, index, model, sectionModelsArray, tableConfig, type } = modelData;

        sectionModelsArray.push( model );

        // size and position model
        //Builder.model._sizeAndPosition( { model, dimensions, type, index }, tableConfig );

        // add model to scene
        _threejs.scene.add( model );

        model.addEventListener( "removed", Builder.prototype.model._dispose );

        // store type of model on model meshes so that we can get this information during raycasting
        model.traverse( function ( child ) {
            child.addEventListener( "removed", Builder.prototype.model._dispose );

            if ( child instanceof THREE.Mesh ) {
                child._modelType = type;
            }
        } );
    },
    // ...
},
//..
/*
* This is the function triggered in componentWillUnmount method of the react component that contains
* the three js scene. In this function I remove all top level children from the scene. This triggers
* the listeners for the remove event and calls the dispose function which releases geometry, materials,
* and removes nested objects from scene triggering their remove events etc...
*/
//...
cleanup: function() {
    // dispose of scene listeners
    Builder.prototype._removeListeners();

    // TODO - implement cache

    // clear the model cache
    //Builder.prototype.model._clearCache();

    // clear the texture cache
    //Builder.prototype.texture._clearCache();

    // remove tableConfig from _threejs object
    _tableConfig = null;

    // reset tableConfig in redux store
    _dispatch( removeTable() );

    // remove all children from scene
    while( _threejs.scene.children.length > 0 ) {
        _threejs.scene.remove( _threejs.scene.children[ 0 ] );
    }

    // remove reference to animations in the heightAnimationController
    if ( _threejs.heightAnimationController ) _threejs.heightAnimationController.dispose();

    // reset _shouldCastRay to false
    _shouldCastRay = false;

    // dispose of controls listeners
    _threejs.controls.dispose();

    // dispose of rendering context
    _threejs.renderer.dispose();

    // reset _threejs object
    _threejs = {
        camera: null,
        controls: null,
        heightAnimationController: null,
        lookAt: null,
        mouseCoords: null,
        raycaster: null,
        raycasterTargetModels: [],
        renderer: null,
        scene: null,
        sceneHeight: 0,
        sceneWidth: 0,
        tableConfig: null
    };

    // cancel requestAnimationFrame from Builder.prototype._animate3js
    cancelAnimationFrame( _animationFrameId );
},
//...

/*
* Dispose function invoked by remove listeners
*/
//...
model: {
    //..
    _dispose: function() {
        const target = arguments[ 0 ].target;

        if ( target instanceof THREE.Mesh ) {
            if ( target.geometry ) {
                target.geometry.dispose();
            }

            if ( target.material ) {
                if ( target.material.bumpMap ) target.material.bumpMap.dispose();
                if ( target.material.envMap ) target.material.envMap.dispose();
                if ( target.material.lightMap ) target.material.lightMap.dispose();
                if ( target.material.map ) target.material.map.dispose();
                if ( target.material.normalMap ) target.material.normalMap.dispose();
                if ( target.material.specularMap ) target.material.specularMap.dispose();

                target.material.dispose();
            }
        } else {
            while ( target.children.length > 0 ) {
                target.remove( target.children[ 0 ] );
            }
        }
    },
    //..
},
//..
在清理过程中,我处理了OrbitControls、WebGLRenderingContext、所有纹理和几何体,并删除了与three.js相关的所有引用

另外,我要更改的react组件只是一个空的div元素。这个无状态组件中没有任何东西可以解释堆中的跳转

我花了很多时间在three.js源代码、stackoverflow和其他网站上查找,试图找出这一点。任何帮助都将不胜感激

编辑:添加到场景中的“模型”是
THREE.scene
的实例,其中包含带有网格的Object3D容器。它们从blender导出并通过
THREE.ObjectLoader
加载。以防万一

编辑#2:在对查找和修复内存泄漏进行了更多研究之后,我有了一个更好的时间表来显示。这在开始时有一个强制垃圾收集,堆位于13522544。大峰值是三个js场景的初始化和模型的加载。这一部分是预料之中的。离开应用程序的三个js部分后,运行处理代码,然后强制进行另一次垃圾收集。在第二次垃圾收集之后,我希望堆与开始时相同。我在同一个地方的应用程序和所有的三个js应该已经清理,但堆现在是14830808;增加1308264人

以下是时间表:

我可以提供任何要求的信息,目前正在旋转车轮:(

编辑#3:

我已经将每次初始化和解构模型的泄漏量减少到大约100kb。不幸的是,快照之间的比较在我的脑海中是模糊的。(不过我对修复泄漏还是新手!)


看起来一切都回到了三个或webpackJsonp。对此有什么建议吗?我想如果我想使用这些库,这可能是我不得不忍受的。在Chrome中,你可以拍摄两个堆快照并比较它们的增量。在最初的强制垃圾收集之后执行一次转储可能是值得的,并且在第二次强制垃圾收集之后再进行一次。然后,您可以在“三”上进行筛选,以查看是否有任何三个特定对象挂起。很难判断这些对象是否是,例如
Float32Array
s(由缓冲区使用),尤其是当原始父对象消失时。@JIM01添加了一个更新。每个模型100kb的大小并不多,除非你加载了数千个模型。(我知道,作为程序员,你希望内存从
0
开始,然后返回到
0
,但JavaScript远不是一个完美的世界)。你在屏幕截图中显示的只是普通JavaScript数组的增量。如果这是你大部分内存被占用的地方,那么你可能会运气不佳,除非你想检查每一个数组,看看是否可以更好地处理这些数组以允许垃圾收集。我通过在从架构的角度来看这个问题。我没有试图分解整个三个js环境,而是将环境放在一边,只添加和删除对象,并根据需要处理纹理和几何体。泄漏与处理渲染器和创建新渲染器有关。我将保留这个问题,以防出现问题ee js god看到了它,可以向我解释我在渲染器上犯了什么错误,或者这是一个三个js的优化问题。你是每次都给它一个画布来使用,还是你在使用它生成的
渲染器.doElement