-
Notifications
You must be signed in to change notification settings - Fork 29.7k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Release zlib callbacks and buffer after processing chunks #6955
Conversation
abca123
to
fecc1ca
Compare
// Don't hold onto the buffer and the callback | ||
// after they have been used. | ||
delete this.buffer; | ||
delete this.callback; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
For efficiency reasons, it's better to set the values to null or undefined. The delete
operator changes the shape (a.k.a. the hidden class) of the object.
Thanks for the feedback. I updated the PR with your suggestions. I was't sure if you preferred to commits to be squash or not, so I pushed a new commit. I'm happy to squash if that's desired. |
// is === this._handle, and that's why it's important to null | ||
// out the values after they are done being used. `this._handle` | ||
// can stay in memory longer than the callback and buffer are needed. | ||
if (this) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Will this
ever be falsy or not req
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
no and no, I think… I guess just asking to change the variable instead of asking for the above comment woud have been easier, sorry
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this
will be falsy when callback
is called from line 547. And, this
will equal newReq
(not req
) when callback is called from the async write on line 623.
Well, I guess req
and newReq
are technically the same value with the current implementation. But this
can definitely be falsy
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@mdlavin newReq === req
because _handle.write()
just returns _handle
(I know it’s confusing)… but yes you can leave it like this then.
@mdlavin This will be squashed anyway when it’s landed, until then you can do it in whatever way you think helps best with reviewing. |
@addaleax it looks like that integration build failed for reasons other than my change. Does that match what you see? |
Yes, the CI failures are unrelated, no need to worry about that. LGTM |
LGTM |
PR-URL: #6955 Reviewed-By: Anna Henningsen <anna@addaleax.net> Reviewed-By: Yuval Brik <yuval@brik.org.il>
Landed in 313ef54, thanks! I believe this is your first commit to core, if so, welcome on board and we hope you find other ways to contribute! |
PR-URL: nodejs#6955 Reviewed-By: Anna Henningsen <anna@addaleax.net> Reviewed-By: Yuval Brik <yuval@brik.org.il>
@addaleax this is my first commit and I'm happy to finally contribute. I'd like to backport this fix to the 4.x series if you think it's a good candidate. The changes are very simple, and I think the change applies cleanly in the 4.x version of zlib too. |
@mdarse I think you can do a backport PR, yes, and I wouldn’t expect much trouble doing that. Make sure to take the commit that landed in master and create a PR with it against |
PR-URL: #6955 Reviewed-By: Anna Henningsen <anna@addaleax.net> Reviewed-By: Yuval Brik <yuval@brik.org.il>
Thanks for the pointers. I opened #7251 as the backport request |
PR-URL: nodejs#6955 Reviewed-By: Anna Henningsen <anna@addaleax.net> Reviewed-By: Yuval Brik <yuval@brik.org.il>
PR-URL: #6955 Reviewed-By: Anna Henningsen <anna@addaleax.net> Reviewed-By: Yuval Brik <yuval@brik.org.il>
PR-URL: #6955 Reviewed-By: Anna Henningsen <anna@addaleax.net> Reviewed-By: Yuval Brik <yuval@brik.org.il>
Checklist
Affected core subsystem(s)
zlib
Description of change
When a long lived zlib compressor is created, using zlib.createDeflateRaw() for example, the compressor will hold onto some functions and buffers longer than it needs to. If there are lots of zlib compressors created, the total allocated space can add up to consume a non-trivial amount of memory.
One example of a real project that can experience this issue is the WS library with per-message deflating: https://github.com/websockets/ws/blob/master/lib/PerMessageDeflate.js . A common user of that is socket.io, which will create possibly create many web sockets. If a socket.io server has lots of web sockets connected with zlib compression, the leaked objects can take memory that could be freed. When New Relic is being used, the amount of memory is even more obvious, because the held closures also keep some New Relic transaction data alive as well.
I know that test cases are desired for changes, but I wasn’t sure about the best way to automate the test for this bug. There are some simple tests that could be added, like checking for certain attributes being null after certain code execution, but I wasn’t sure if that was useful enough. I’m open to suggestions. In place of a unit test, I created a standalone example that can demonstrate the problem here: https://gist.github.com/mdlavin/334338eb801bd3243679db89c219c7ac
Before the fix, the output looks like this:
After the fix, the number of Closures and Uint8Arrays are reduced by 1 each because the memory can be reclaimed by the GC.