Although there was an issue before about this kind of leak: https://github.com/mui-org/material-ui/issues/8019
That was apparently resolved, so thought I'd open up a new issue if its perhaps something new this time.
I'm using material-ui in my server-rendered React app.
I've been searching for a memory leak in my Node.js server lately. It was getting pretty severe, my servers are leaking 100s of MB every day. Through this process I've been doing memory dumps using Chrome dev tools and looking at the object allocations between Snapshots to see what is retained (after many requests and abuse to the server).
What I've found is plenty of retained data of (array) type which seems to be pointing to sheetsManager which is also pointing to material-ui and the withStyles.js file:

If I open up one of those sheet things, I get this:

Which clearly points to jss and you can see some key which says paperAnchorTop which sounds very much like material-ui.
There are 213 of these entries in the array which have been retained. Which sounds like the same amount of page requests which I made to my server.
The entry to my app on both server and client looks like this:
export function baseReact(stores, router, userAgent) {
const muiThemeComponents = {};
// For SSR, user agent passed from request
if (userAgent) {
muiThemeComponents.userAgent = userAgent;
}
const muiTheme = createMuiTheme();
return (
<ErrorBoundary>
<Provider {...stores}>
<MuiThemeProvider theme={muiTheme}>{router}</MuiThemeProvider>
</Provider>
</ErrorBoundary>
);
}
Which on the server is rendered like so:
const html = reactDomServer.renderToString(
baseReact(ctx.state.mobx, <RouterContext {...props} />, userAgent)
);
I think that's pretty stock standard and shouldn't be causing any leaks just like that.
Since the large majority of retained information seems to be coming from this sheet :: StyleSheet object, I think that something is leaking either with jss or the way material-ui deals with styles itself.
| Tech | Version |
|--------------|---------|
| Material-UI | 1.0.0-beta.40 |
| React | 16.3.1 |
| browser | Chrome |
| Node.js | 9.8.0 |
@lostpebble Thanks for the report. We better find the root of the issue. Nothing pops out of my mind with the provided information. Are you able to provide a reproduction repository?
Hi @oliviertassinari , cool I've put together a small repo quick. Should hopefully be able to reveal something: https://github.com/lostpebble/material-ui-potential-style-leak
This is actually becoming quite a large issue at the moment. My servers are resetting at least twice a day now because of the stagnant memory build-up. And from what I can see, it's something called sheetsManager which is the culprit. I think this is something used by MuiThemeProvider, as I see on the server-rendering guide it's something passed into MuiThemeProvider:
<JssProvider registry={sheetsRegistry} generateClassName={generateClassName}>
<MuiThemeProvider theme={theme} sheetsManager={new Map()}>
<App />
</MuiThemeProvider>
</JssProvider>
I'm not really sure about the internal workings. I'll try dig into that soon. But perhaps MuiThemeProvider needs another prop such as serverRendering which causes sheetsManager to not be persisted (I assume some kind of persistence is needed on the client side) after a single render.
@lostpebble I just had a look at your reproduction repository. There is definitely a leak. It's easy to fix. I'm looking at how to prevent people from falling into this trap in the first place.
Ahh, such a silly little thing for me to overlook. Thanks for the fix @oliviertassinari ! Glad we got to the bottom of it.
I added a sheetsManager prop to MuiThemeProvider in the repo and the leak has disappeared.
@lostpebble I'm confused by your last comment - in the example code you provide I see sheetsManager={new Map()} but then you say later that you fixed this by "adding a sheetsManagerprop to MuiThemeProvider"
But weren't you already doing this?
We're leaking as well, and we are currently putting a new Map() into our MuiThemeProvider sheetsManager prop
I've got same situation in chrome dev tools. Application runs to heap memory limit when under load. Using latest Next.js and material-ui, installed according to https://github.com/mui-org/material-ui/tree/master/examples/nextjs . @oliviertassinari could you advice where to look at?
@dmdb Please include the versions you are using where the memory leaks (just base components or own styles using makeStyles, withStyles etc?)
The styling solution does not support strict mode or concurrent mode yet. In those modes styles can leak.
I use this to custommise styles:
import { withStyles } from '@material-ui/core/styles';
And this some of related packages versions:
"@material-ui/core": "^3.9.3",
"@material-ui/icons": "^3.0.2",
"jss": "^9.8.7",
"styled-jsx": "^3.2.0",
"next": "^8.0.4",
"react-jss": "^8.6.1",
Is there all up to date in nextjs integration example? Iam also did'nt get how @lostpebble solved this :(
If there is no known pitfalls about this, I will try to reproduce it in clean repo. Thanks in advance!
@dmdb You would need to further isolate the problem. The examples in the /examples folder are up to date.
I've found memory leak in my application, and it was my bad code. Nextjs example got no problems (tested while tried to reproduce :) ) Anyway, sorry and thanks for attention!
@dmdb Hey ya, can you give a little more info on what the leak was? I'm running an almost exact version-by-version same as you for the modules you listed and I've followed the example to implement jss server-side rendering with next.js but I'm seeing lots of retained objects (probably is my code but wanted to check with you)
Hey guys,
we've been running with Mui (1.5.0) for a while and have discovered that Mui and/or styled components leak in a SSR env on redirects.
Our SSR render function was as follows:
export default ({clientStats}) => async (req, res) => {
const store = await configureStore(req, res);
const sheet = new ServerStyleSheet();
const sheetsRegistry = new SheetsRegistry();
const generateClassName = createGenerateClassName();
const theme = createMuiTheme();
if (!store) return null; // no store means redirect was already served
// More stuff..
Where configureStore would own the logic of calling res.redirect(302, path) and return null in case of a redirect. If there was no redirect the render returned our HTML with styles injected.
_Regular requests without triggering redirects did not leak._
However, if there was a redirect, the code above leaked about 500mb / 10k redirects. Fixing was as easy as dropping style sheets and themes builds on redirects. It's kind of silly to build these on redirects, and obviously a flaw on our end performance wise, but I don't think there should be reason for the retained memory. Maybe this can help you guys narrow down the issue.
Fix:
export default ({clientStats}) => async (req, res) => {
const store = await configureStore(req, res);
if (!store) return null; // no store means redirect was already served
const sheet = new ServerStyleSheet();
const sheetsRegistry = new SheetsRegistry();
const generateClassName = createGenerateClassName();
const theme = createMuiTheme();
@kristfal Does it still happen in v4? What reference retains the objects in memory?
I don't if we are the same problem, but it works for me after I add the props sheetsCache={null}
<StylesProvider sheetsManager={sheetsManager} sheetsRegistry={sheetsRegistry} sheetsCache={null}>
<StaticRouter location={req.url} context={{}}>
<App />
</StaticRouter>
</StylesProvider>
i have the same issue on browser #21528.
Most helpful comment
@lostpebble I just had a look at your reproduction repository. There is definitely a leak. It's easy to fix. I'm looking at how to prevent people from falling into this trap in the first place.