-
Notifications
You must be signed in to change notification settings - Fork 114
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
Address memory leak in JSON parse and stringify #835
Merged
Merged
Changes from 1 commit
Commits
Show all changes
7 commits
Select commit
Hold shift + click to select a range
11a97e9
Address memory leak in JSON parse and stringify
jeffcharles 1bbc149
Merge branch 'main' into jc.remove-memory-leak-in-json
jeffcharles f803afc
Only perform function lookups if we need to call the fallback
jeffcharles 71bf2b3
Merge branch 'main' into jc.remove-memory-leak-in-json
jeffcharles 3539d20
Replace fallback for stringify with rquickjs builtin
jeffcharles 71cf16f
Use unstable key for default JSON.parse function
jeffcharles fe8ec83
Linting
jeffcharles File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
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.
Out of curiosity, did you perform any benchmarking around this change? I don't think we have any fuel tests that cover this scenario, right? The reason why I suspect that this might affect performance is that every time any of these functions gets called, it'll result in a property access lookup, which is generally no the fastest operation.
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.
The other consideration here is that this approach is going to pollute the global namespace, which might end up being used unofficially as an API (e.g.,
globalThis.__javy_json_stringify
) which would be hard to deprecate down the line. Highly unlikely in my opinion but could happen.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.
The readme test invokes
JSON.parse
andJSON.stringify
. Fuel count on main is 253,787 and on this branch is 256,759 so ~1.17% increase given both method calls. I don't love that result and I'm open to other ideas for how to get rid of the memory leak.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.
Another option is I could perform the fallback method look-up only if we can't call the method we're overriding it with (that is, if they pass more than one argument). Calls with more than one argument still be a bit more expensive than would otherwise be the case but at least the typical one argument case should be just as fast as it is now.
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.
Looks like the difference for a single argument is 254,153 on the branch versus 253,787 on main (~0.14% increase). I'm not sure why there's still an increase.
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.
An alternative thought: have you considered introducing a cargo feature exclusively available for fuzzing? I suspect that might make your changes easier to integrate without making modifications that will be globally accessible for every user (e.g., potential performance impact and modifications to the global object).
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.
Then the code we're fuzzing would be different code than the code users would use. Granted parts of the code being fuzzed would be the same but other parts would be different. And I suppose fuzzing some code is arguably better than no code being fuzzed. But if we do go this route, I would want to document in the project that enabling the JSON builtins introduces a known memory leak.
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.
Clarification: when I meant fuzzing I meant to allow you to land this change with less friction and therefore help you detect the leak and how to fix it. From the description in your change it seems that there isn't full confidence that the JSON pieces are the cause of the leak?
So the suggestion of the cargo feature is mostly to unblock any future investigations.
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.
Because ideally I agree that we shouldn't modify the code one way or another to continuously fuzz.
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.
So there's a few things different things going on with the memory with a number of unknowns. I know the move of the QuickJS context as referenced by the
Function
into theMutFn
introduces a memory leak. That can be verified that by turning on thedump-leaks
Cargo feature onrquickjs
or by removing theManuallyDrop
on the properties of the JavyRuntime
and allowing the runtime to drop (an assertion will fire that the list of GC objects is not empty). What I don't know yet is if that memory leak only occurs once when a Javy runtime is created or if occurs every timeJSON.parse
andJSON.stringify
are called. If it only happens once, then I don't think that would cause the memory growth we're seeing while fuzzing because we create a single runtime once. If it happens every timeJSON.parse
orJSON.stringify
is called, then that would cause or exacerbate the memory growth we're seeing. But it happening once would still be a problem, independent of fuzzing, if someone wanted to drop the rquickjs runtime or rquickjs context for whatever reason. They can't drop either the rquickjs runtime or rquickjs context anyway with the APIs we currently expose but it would, for example, preclude being able to drop them in the future if we were to update the Javy crate to remove thoseManualDrop
s and put it on the JavyRuntime
instance in thejavy-plugin-api
.