-
Notifications
You must be signed in to change notification settings - Fork 32
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
Possible memory leak? #33
Comments
Thanks, this memory leak seems like an important issue. |
After some testing, it seems to be related to the following segment of code: (executing.py:809-816) def finder(code):
for const in code.co_consts:
if not inspect.iscode(const):
continue
if matches(const):
code_options.append(const)
finder(const) When I comment the last line (i.e. @classmethod
def executing(cls, frame_or_tb):
"""
Returns an `Executing` object representing the operation
currently executing in the given frame or traceback object.
"""
if isinstance(frame_or_tb, types.TracebackType):
# https://docs.python.org/3/reference/datamodel.html#traceback-objects
# "tb_lineno gives the line number where the exception occurred;
# tb_lasti indicates the precise instruction.
# The line number and last instruction in the traceback may differ
# from the line number of its frame object
# if the exception occurred in a try statement with no matching except clause
# or with a finally clause."
tb = frame_or_tb
frame = tb.tb_frame
lineno = tb.tb_lineno
lasti = tb.tb_lasti
else:
frame = frame_or_tb
lineno = frame.f_lineno
lasti = frame.f_lasti
code = frame.f_code
key = (code, id(code), lasti)
executing_cache = cls._class_local('__executing_cache', {})
try:
args = executing_cache[key]
except KeyError:
* def find(source, frame, lineno, lasti, retry_cache):
node = stmts = decorator = None
tree = source.tree
if tree:
try:
stmts = source.statements_at_line(lineno)
if stmts:
if is_ipython_cell_code(code):
for stmt in stmts:
tree = _extract_ipython_statement(stmt)
try:
node_finder = NodeFinder(frame, stmts, tree, lasti)
if (node or decorator) and (node_finder.result or node_finder.decorator):
if retry_cache:
raise AssertionError
# Found potential nodes in separate statements,
# cannot resolve ambiguity, give up here
node = decorator = None
break
node = node_finder.result
decorator = node_finder.decorator
except Exception:
if retry_cache:
raise
else:
node_finder = NodeFinder(frame, stmts, tree, lasti)
node = node_finder.result
decorator = node_finder.decorator
except Exception as e:
# These exceptions can be caused by the source code having changed
# so the cached Source doesn't match the running code
# (e.g. when using IPython %autoreload)
# Try again with a fresh Source object
if retry_cache and isinstance(e, (NotOneValueFound, AssertionError)):
return find(
source=cls.for_frame(frame, use_cache=False),
retry_cache=False,
* frame=frame, lineno=lineno, lasti=lasti
)
if TESTING:
raise
if node:
new_stmts = {statement_containing_node(node)}
assert_(new_stmts <= stmts)
stmts = new_stmts
return source, node, stmts, decorator
* args = find(source=cls.for_frame(frame), frame=frame, lineno=lineno, lasti=lasti, retry_cache=True)
executing_cache[key] = args
return Executing(frame, *args) After this debugging session with prints and code commenting, I think that it would be a better idea to use a proper debugger or find a way to print |
I don't think that actually fixed anything, I think that simply broke However I do think that you may have found something with that second part of your comment. It sounds like I also wonder if this: try:
args = executing_cache[key]
except KeyError: instead of something like |
I just tried to replace it with the code below but it didn't work. if key in executing_cache:
args = executing_cache[key]
else: This will require a proper memory profiler or debugger to fix. |
Have you tried looking at Do you think you can make a script demonstrating the reference leak without Qt? |
Not yet. I will do it later when I have time.
…On Mon, 21 Mar 2022 at 11:25, Alex Hall ***@***.***> wrote:
Have you tried looking at gc.get_referrers()?
Do you think you can make a script demonstrating the reference leak without Qt?
—
Reply to this email directly, view it on GitHub, or unsubscribe.
You are receiving this because you authored the thread.Message ID: ***@***.***>
|
Thank you! |
I could not solve by poking around the variables that refer to my object but I did discover that explicitly calling the garbage collector after using the result worked. See gruns/icecream#118 (comment) |
Thanks for investigating! That seems to suggest that there isn't really a proper memory leak, and a fuller program running for longer and doing more in general would automatically do the necessary garbage collection. Maybe there's a reference cycle that prevents the frame from being collected instantly. |
Can you see what happens if you:
|
I'm currently working on the 3.11 branch and checking a lot of files. The tests consume ~10GB memory after ~30 minutes. This might not be related to this issue and only a bug in the 3.11 branch, but I thought it is worth mentioning here. |
That's likely just because you're checking so many fies. A |
While debugging why icecream has issues with Qt, I discovered that
executing.Source.executing
keeps some sort of hidden reference to the local variables of the function called. In the code sample below, the issue is noticeable by the fact thatmethod0
andmethod1
show only one window whilemethod2
shows two windows (one being basically a black screen). This happens because in Qt widgets remain shown while someone is holding a reference to them.The text was updated successfully, but these errors were encountered: