-
-
Notifications
You must be signed in to change notification settings - Fork 33.6k
src: track memory retainer fields #26161
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
Conversation
src/memory_tracker-inl.h
Outdated
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.
I feel like if we want to provide this as an utility, it might make sense to perform something similar to Track() and/or re-use it? That would be nice if we do want to see these fields as full “objects” on their own, and want to show them that way in heap dumps.
Then again, I think you’re making a good point about this approach being a good way to address the problem itself; I just didn’t think of a direct approach like this. 😄 Adding 2 calls à la thread_stopper_.MemoryInfo(tracker); in #26099 would also be a good way to address this, as far as that PR is concerned in particular?
56f5969 to
6055693
Compare
|
@addaleax - pushed a change; ptal. I tested the two inline fields in question using { name: 'Node / Worker',
isRoot: true,
size: 200,
edges:
[ { name: 'wrapped',
to:
{ name: '<JS Node>',
isRoot: false,
size: 0,
edges: [Array],
value: [Worker] } },
{ name: 'parent_port',
to:
{ name: 'Node / MessagePort',
isRoot: true,
size: 88,
edges: [Array] } },
{ name: 'on_thread_finished_',
to:
{ name: 'Node / AsyncRequest',
isRoot: false,
size: 72,
edges: [Array] } },
{ name: 'thread_stopper_',
to:
{ name: 'Node / AsyncRequest',
isRoot: false,
size: 72,
edges: [Array] } } ] } |
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.
Thanks, LGTM!
|
CI is green … Maybe somebody has time to give this a second review? |
src/memory_tracker.h
Outdated
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.
I am not sure what this is trying to achieve, is there a use case of this API?
Normally the size of the pointer to the retainer should be accounted into the self size of the parent but self size of the retainer should not be accounted, so I can't tell when it's necessary to do a subtraction - maybe there is a misuse of the API?
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.
If this is trying to address the issues that came up from #26099 there are two ways to solve that:
- Do not use
SET_SELF_SIZE(Worker), which is merely a convenience macro that implementsWorker::SelfSize()withsizeof(Worker)to calculate the self size of the worker - that's where we encounter the first tracking of the inline field. Instead, overrideWorker::SelfSize()to exclude the size of the non-pointer fields, and track them later inWorker::MemoryInfo(). - Keep using
SET_SELF_SIZE(Worker)for the initial calculation, but rename this method into something likeSplitNonPointerField()(SplitStackAllocatedField()?), because this does not necessarily add more memory into the tracker - instead it's splitting off existing memory that have already been tracked, and potentially adding more (if theretaineroverridesMemoryInfo()to add additional fields that are not accounted by asizeof())
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.
@joyeecheung - thanks.
Probably these many scenarios exists (with a component cmp and a container cnt with cnt a retainer):
cmpis a normal primitive field: self size ofcntcovers size ofcmpcmpis a composite object (with or without primitive fields): self size ofcntcovers size ofcmpcmpis a pointer to a composite object: self size ofcntcovers size ofcmp(size of a pointer)cmpis a composite object (with pointer type fields): self size ofcntcovers size ofcmp, not the dynamic memory [ example: messageport in worker ]cmpis a composite retainer object (with pointer types): [ current use case ]cmpis a pointer to a retainer object (with pointer types): [ none at the moment? ]
We have Worker with 200 bytes excluding 2 inline AsyncRequest fields of 72 bytes each. So the question is: should we add those to the worker (because those belongs to worker) or add separately - as they are retainers themselves.
If I follow your approach 1, we need to perform that in all such scenarios? in which case arguably the parent retainer (Worker's referencing object if any) could do the same for worker, and potentially render the Retainer abstraction meaningless?
second proposal looks fair to me, but would love to hear from @addaleax as well. Can SplitStackAllocatedField be somewhat misleading, as there is no memory that is stack-allocated here?
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 this does not necessarily add more memory into the tracker - instead it's splitting off existing memory that have already been tracked, and potentially adding more (if the
retaineroverridesMemoryInfo()to add additional fields that are not accounted by asizeof())
I’m not sure, but I don’t really see this as a issue/as not “tracking” these fields? Then again, re-naming is not a big deal, so I’m okay with anything.
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.
should we add those to the worker (because those belongs to worker) or add separately - as they are retainers themselves.
That depends on whether we want to display AsyncRequest separately as a Node referenced by the Worker node, and any other fields referenced by those AsyncRequests recursively.
If I follow your approach 1, we need to perform that in all such scenarios? in which case arguably the parent retainer (Worker's referencing object if any) could do the same for worker, and potentially render the Retainer abstraction meaningless?
The abstraction helps adding nodes recursively instead of tracking everything from the root, allowing the MemoryRetainer implementations define how they want their fields to be tracked.
Can
SplitStackAllocatedFieldbe somewhat misleading, as there is no memory that is stack-allocated here?
Right, the fields are not really stack allocated if the whole thing is not stack allocated.
I’m not sure, but I don’t really see this as a issue/as not “tracking” these fields?
I am fine with the current naming if the comments are changed to reflect when this method should be used and what issue it is trying to address. Use when a retainer is embedded in another. and Reduce the size of memory from the container so that retentions are accounted properly. is too ambiguous IMO, the core of the issue this method is trying to solve arise from that the parent does not implement its SelfSize() correctly to exclude the memory that is supposed to be tracked as the self size of another node.
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.
@joyeecheung - thanks, I will amend the comments, aligning with above conversation.
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.
@joyeecheung - amended the comment, ptal!
If retainers are embedded in retainers, direct tracking those lead to double tracking. Instead, use a special tracker that adjusts the tracking for the container object. PR-URL: nodejs#26161 Reviewed-By: Anna Henningsen <[email protected]>
726350f to
17b4949
Compare
|
landed as 17b4949 |
If retainers are embedded in retainers, direct tracking those lead to double tracking. Instead, use a special tracker that adjusts the tracking for the container object. PR-URL: #26161 Reviewed-By: Anna Henningsen <[email protected]>
If retainers are embedded in retainers, direct tracking
those lead to double tracking. Instead, use the base tracker
to track the field objects.
cc @addaleax [ I should admit that I don't have full understanding of this feature, so let me know if this needs rewrite ]
Checklist
make -j4 test(UNIX), orvcbuild test(Windows) passes