Memory leak on React > 16.2.5
mgreer opened this issue Β· 22 comments
Do you want to request a feature or report a bug?
Possible bug, or unexpected change in internal behavior
What is the current behavior?
We are seeing a substantial memory leak in our codebase in versions of React > 16.5.2. Bisecting the issue, it seemed to appear at commit 7bee9fb.
If the current behavior is a bug, please provide the steps to reproduce and if possible a minimal demo of the problem. Your bug will get fixed much faster if we can run your code and it doesn't have dependencies other than React. Paste the link to your JSFiddle (https://jsfiddle.net/Luktwrdm/) or CodeSandbox (https://codesandbox.io/s/new) example below:
Our apologies, we are unable to fully isolate the issue to a minimal demo, and suspect it is not a bug but a behavior change which our code relied on. The leak seems to be in the messages area, where many components are created and released quickly.
What is the expected behavior?
Old fibers should be garbage collected, but cannot be. Components are retained in memory.
We have run the affected code area in StrictMode, which allowed us to identify some event leakage but even when this was addressed (by moving listeners to componentDidMount from the constructor) the memory leak continues. There are some findDomNode usages remaining, but even if we move those it does not fill the leak.
Which versions of React, and which browser / OS are affected by this issue? Did this work in previous versions of React?
16.5.3+
We are seeking suggestions on what internal could be causing retention of components and nodes by our code, and better ways to isolate the issue.
Thank you!
-Mike
We've encountered similiar issue on 16.6.3.
Allocation instrumentation on timeline shows that our application allocates a lot of FiberNode objects:
And timeline shows that they are never collected, heap grows up without GC firing when we expecting it:
At the same time, timeline on 16.8.0-alpha.1 (16.7.0 shows the same) GC seems to fire correctly:
May be it was already fixed by this?
0e9cb3f It's tagged 16.7.0
Sadly no, that had been our hope but leak persists.
We had similar problem but we fixed that by upgrading react-dom and react lib to 16.7
The only relevant fix I'm aware of is #14276 which got into 16.7.0. We'd need a reproducing case to help more.
Remember to test production version, and also that we retain a reference to deleted children for one more update (#12420 (comment)).
Updating from React 16.6 to 16.8 drastically improved our memory usage, but it's still there, slow and inexorable.
Currently I have installed react and react-dom 16.8.6.
More info: I'm using Electron, BlueprintJS and Redux, I have a polling component that updates itself making some http requests and dispatching a Redux action.
From devtools I can see a lot of FiberNode and they are never deleted, they just keep growing.
My naive implementation of the component it's something like this:
class MyComponent extends Component {
componentDidMount = () => {
this.interval = setInterval(this.tick, 1000);
};
componentWillUnmount = () => {
clearInterval(this.interval);
};
tick = async () => {
if (this.updating) {
return;
}
this.updating = true;
try {
// dispatch async (thunk middleware) redux action,
// this will trigger an API and maybe a re-render this component
await this.props.doUpdate();
} catch (e) {
/* handle error */
}
this.updating = false;
};
render() {
<span>{this.props.myState}</span>
}
}
if I can help providing any information, just let me know. Thanks!
I have very similar memory leak with video and audio elements, perhaps related in some way? Do you find a lot of detached nodes if you create a heap snapshot under memory and search for detached in the class filter? You can check out my issue here, which is very easy to reproduce with a simple demo: #15583
We have a somewhat similar memory leak (although we're experiencing it on all React 16.x versions) where there are a lot of detached nodes that never get garbage collected. The issue goes away if we downgrade to React 15.x.
You can see the documentation in this issue (along with a reproducible example): #15637
I've got the same problem. Any one solved this problem except demotionοΌ
The only relevant fix I'm aware of is #14276 which got into 16.7.0. We'd need a reproducing case to help more.
Remember to test production version, and also that we retain a reference to deleted children for one more update (#12420 (comment)).
This case maybe helpful.
Potential memory leak when navigating between pages
Here is a repro: https://codesandbox.io/s/compassionate-fermat-3cq5b
Simply uncomment line 7 and the page will continue to grow in heap until it OOMs.
Not sure if related somehow, but I've been seeing this message a lot and the app takes a long time to hot reload during local development:
error [BABEL] Note: The code generator has deoptimised the styling of
Navigating to certain pages just hangs the browser and I have to kill the task. This seems to have started recently when we updated some packages.
Any update on this ?
Upgrading to 16.9.0
completely fixes the problem for me. π
Oddly, upgrading to 16.9.0-rc.0
decreases my memory leak by an order of magnitude (25MB -> 3MB per page transition), but only when NODE_ENV=development
. When I use NODE=ENV=production
, the leak reappears.
What minifier are you using? Weβve seen perf issues caused by a buggy minifier version.
Very interesting, @gaearon, thanks for the tip. I'm using uglifyjs-webpack-plugin@1.2.4
and uglify-js@3.4.10
. Disabling minification via module.exports.optimization.minimize: false
in webpack.config.js
cuts the leak from 10MB per tree remount to 5MB. I will continue investigation.
EDIT: I was able to resolve a couple memory leaks in my own application. Unfortunately this didn't move the needle. However, applying #15157 cut the leak from 5MB to <1MB. Hopefully that MR will go in soon :)
EDIT: Issue is ongoing. Here are a collection of heap snapshots with 10 tree remounts in between each snapshot. This is without #15157 applied. It appears that react is the culprit, but perhaps devtools is misleading here, and user-land memory leaks will appear to originate in react? Any advice greatly appreciated! :)
@gaearon and all,
I can easily reproduce it using react-dnd, here are my steps when I was working on following nested IDE projects, from below image we can see a lot of framed boxes, these boxes can be dragged and dropped.
if I use both drag and drop, will raise memory leak
my code:
-
setup drop
`
const [{ isOverCurrent }, drop] = useDrop({
accept: [DND_IDE_NODE_TYPE, DND_IDE_SCHEMA_TYPE, DND_IDE_DATASOURCE_TYPE],
canDrop: (item: DragItem, monitor: DropTargetMonitor) => {
if (!refDrop.current) {
return false;
}
const draggingNode = item.uinode;
const hoverNode = uinode;if (draggingNode) { let cachedActiveTab = JSON.parse( localStorage.cachedActiveTab || "{}" ); if (!_.isEmpty(cachedActiveTab)) { if ( draggingNode.schema && draggingNode.schema.$template && draggingNode.schema.$template === cachedActiveTab.tabName ) { return false; } } // Don't replace items with themselves if ( draggingNode === hoverNode || !monitor.isOver({ shallow: true }) ) { return false; } } if (item.type === DND_IDE_NODE_TYPE) { if (!dndNodeManager.canDrop(draggingNode, hoverNode)) return false; const hoverBoundingRect = refDrop.current!.getBoundingClientRect(); // Determine mouse position const clientOffset = monitor.getClientOffset(); // detect update region style const regionName = regionDetector.detectCurrentRegion( clientOffset as XYCoord, hoverBoundingRect ); // if center, and collapsed, don't go on insert if (regionName === "center" && isCollapsed) return false; // judge center // it's not a container if ( regionName === "center" && !dndNodeManager.canDropInCenter(hoverNode) ) { return false; } } return true;
},
hover: async (item: DragItem, monitor: DropTargetMonitor) => {
if (!monitor.canDrop()) return;let regionStyles; switch (item.type) { case DND_IDE_NODE_TYPE: // Determine rectangle on screen const hoverBoundingRect = refDrop.current!.getBoundingClientRect(); // Determine mouse position const clientOffset = monitor.getClientOffset(); // detect update region style const regionName = regionDetector.detectCurrentRegion( clientOffset as XYCoord, hoverBoundingRect ); if (regionName) { if (regionName === "center") { regionStyles = { border: "3px solid #f00" }; } else { const mapBorder = { up: "top", down: "bottom", left: "left", right: "right" }; const borderName = `border${_.upperFirst( mapBorder[regionName] )}`; regionStyles = { [borderName]: "3px solid #f00" }; } setOverClass("node"); setBorder(regionStyles); } break; case DND_IDE_SCHEMA_TYPE: regionStyles = { border: "3px solid #80c35f", backgroundColor: "#def9d1" }; setOverClass("schema"); setBorder(regionStyles); break; case DND_IDE_DATASOURCE_TYPE: regionStyles = { border: "3px solid #80c35f", backgroundColor: "#40a9ff" }; setOverClass("datasource"); setBorder(regionStyles); break; }
},
drop: async (item: DragItem, monitor: DropTargetMonitor) => {
if (!monitor.canDrop()) return;
const draggingNode = item.uinode;
const targetNode = uinode;switch (item.type) { case DND_IDE_NODE_TYPE: // Determine rectangle on screen const hoverBoundingRect = refDrop.current!.getBoundingClientRect(); // Determine mouse position const clientOffset = monitor.getClientOffset(); // detect update region style const regionName = regionDetector.detectCurrentRegion( clientOffset as XYCoord, hoverBoundingRect ); const insertMethodName = `insert${_.upperFirst(regionName)}`; if (dndNodeManager[insertMethodName]) { dndNodeManager[insertMethodName](draggingNode, targetNode); } break; case DND_IDE_SCHEMA_TYPE: case DND_IDE_DATASOURCE_TYPE: if (_.isObject(item.schema)) { dndNodeManager.useSchema(targetNode, item.schema); } break; } setDropNode(draggingNode); updateSchema(targetNode.schema);
},
collect: monitor => ({
isOverCurrent: monitor.isOver({ shallow: true })
})
});
`
This issue has been automatically marked as stale. If this issue is still affecting you, please leave any comment (for example, "bump"), and we'll keep it open. We are sorry that we haven't been able to prioritize it yet. If you have any new additional information, please include it with your comment!
Closing this issue after a prolonged period of inactivity. If this issue is still present in the latest release, please create a new issue with up-to-date information. Thank you!