3d-force-graph
3d-force-graph copied to clipboard
Cleaning problems
Hi,
We use 3D-force-graph to display small (80 nodes) to large graphs (thousands nodes) and thank you very much for this library ! We appreciate the ease of use, configuration and speed of the result.
But we have problem when trying to clean all the stuff in order to limit memory footprint of our application.
We use javascript, not TS. The way we use it is as follow :
- creation via this script :
Graph = ForceGraph3D() (elem) .graphData(convertToGraph3D(relations, true)) .nodeAutoColorBy('nature') .numDimensions(3) .showNavInfo(false) .height(dimensions[0]) .width(dimensions[1]) .onNodeHover(node => elem.style.cursor = node ? 'pointer' : null) .onNodeDragEnd(node => { node.fx = node.x; node.fy = node.y; node.fz = node.z; }) .nodeThreeObject(node => { // image ? let opacity = 0.2; let hasImage = false; if (node.mediaList && node.mediaList.length > 0) { hasImage = true; opacity = 0; } // use a sphere as a drag handle const obj = new THREE.Mesh( new THREE.SphereGeometry(5), new THREE.MeshBasicMaterial({ depthWrite: false, transparent: true, opacity: opacity}) ); objectsToDelete.push(obj);
// add image sprite as child
if (hasImage) {
const imgTexture = new THREE.TextureLoader().load("photo/" + node.mediaList[0]);
objectsToDelete.push(imgTexture);
const material = new THREE.SpriteMaterial({map: imgTexture});
objectsToDelete.push(material);
const spriteImg = new THREE.Sprite(material);
objectsToDelete.push(spriteImg);
spriteImg.scale.set(30, 30);
obj.add(spriteImg);
}
// add text sprite as child
if (node.name != "") {
let v = node.name;
let maxLName = 20;
if(v.length > maxLName) v = v.substr(0, maxLName) + "...";
const spriteTxt = new SpriteText(v);
objectsToDelete.push(spriteTxt);
spriteTxt.color = node.color;
spriteTxt.textHeight = 8;
// translate text if there is a photo
if (hasImage) {
spriteTxt.translateY(-30)
} else {
spriteTxt.translateY(-10)
}
obj.add(spriteTxt);
}
return obj;
});
- we use a var objectsToDelete to keep all the THREE objects created
- we tried the following function to clean as much as we can :
function resetGraph() { // Graph.graphData({ // "nodes": [], // "links": [] // }); objectsToDelete.forEach (o => { if (o.geometry) { o.geometry.dispose(); } if (o.material) { if (o.material.length) { for (let i=0; i<o.material.length; i++) { o.material[i].dispose(); } } else { o.material.dispose(); } } if (o._texture) { o._texture.dispose(); } if (o.dispose) { o.dispose(); } o = null; }); objectsToDelete = []; Graph.renderer() && Graph.renderer().renderLists && Graph.renderer().renderLists.dispose() Graph.renderer() && Graph.renderer().dispose() Graph.scene = null Graph.camera = null Graph = null; }
But when we monitor memory via Chrome process monitor there is no noticeable difference if the resetGraph() is called or not. The memory continue to grow each time we load another graph.
Is there something we can do ? Regards
@git3dGraph007 the module is already doing garbage collection of objects internally, similarly to what you're doing with resetGraph()
, that's why you don't see much difference between the two modes.
It's more or less equivalent to just doing .graphData({ nodes: [], links: [] })
.
As for why the memory consumption still increases, it remains challenging to perform a complete 100% garbage collection in WebGL, so some remnants might still remain registered and keep on consuming memory if it's available. Does the memory consumption eventually decrease at some point when the browser garbage collection might be triggered?
Thanks for your reply :)
I was reading this : https://threejs.org/docs/index.html#manual/en/introduction/How-to-dispose-of-objects and tried to apply to my usage of your library, but as you said you already do this.
When i manually trigger the GC, i see little decrease in memory usage. But after loading 4 or 5 graphs with 500 nodes and links, the Chrome Tab use 1G of Ram (and about 250M for Javascript RAM)
Was thinking about loading the graph in a iframe and destroy that iframe when loading another graph. Maybe it will release all the memory used ?
@git3dGraph007 Were you able to make any progress on this, I am having the same issue.
@git3dGraph007 @Alexithemia Any progress? Same issue!
me too
Unfortunatelly I have to pause animation and set graph data as empty. @vasturiano, is it possible to add some "destroy" method which will makes sure that everything is removed? As you may see from screenshot, the "animate" function is called even if canvas and graph instance were removed at 20 s
onUnmounted(() => { // this code prevent force graph from taking memory even if canvas was destroyed forceGraph.value!.pauseAnimation().graphData({ nodes: [], links: [] }); });
@kyrylo93 there is a _destructor
method, that you can invoke like myGraph._destructor()
. But it is essentially equivalent to what you're already doing:
https://github.com/vasturiano/3d-force-graph/blob/b7722c50362ec6c993868005fafeca36afd63028/src/3d-force-graph.js#L179-L182