Skip to content

Fix Ray Tune memory leak by clearing dangling ObjectRef#64236

Open
jashwanth-reddy-g wants to merge 2 commits into
ray-project:masterfrom
jashwanth-reddy-g:fix/ray-tune-memory-leak-64231
Open

Fix Ray Tune memory leak by clearing dangling ObjectRef#64236
jashwanth-reddy-g wants to merge 2 commits into
ray-project:masterfrom
jashwanth-reddy-g:fix/ray-tune-memory-leak-64231

Conversation

@jashwanth-reddy-g

Copy link
Copy Markdown

Thank you for contributing to Ray! 🚀
Please review the Ray Contribution Guide before opening a pull request.

⚠️ Remove these instructions before submitting your PR.

💡 Tip: Mark as draft if you want early feedback, or ready for review when it's complete.

Description

Briefly describe what this PR accomplishes and why it's needed.

Related issues

Link related issues: "Fixes #1234", "Closes #1234", or "Related to #1234".

Additional information

Optional: Add implementation details, API changes, usage examples, screenshots, etc.

@jashwanth-reddy-g
jashwanth-reddy-g requested a review from a team as a code owner June 20, 2026 18:44

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request updates the set_ray_actor method in trial.py to set self._default_result_or_future to None when ray_actor is not provided. The reviewer suggests conditionally clearing this attribute only if it is a ray.ObjectRef to prevent discarding useful metadata when it has already been resolved to a dictionary.

Important

The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.

Comment thread python/ray/tune/experiment/trial.py Outdated
Comment on lines +685 to +686
else:
self._default_result_or_future = None

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

If self._default_result_or_future has already been resolved to a dict (e.g., via last_result or _get_default_result_or_future), it is a plain Python dictionary and does not hold any Ray object references, so it cannot cause a Ray memory leak. Clearing it unconditionally discards useful metadata (like node_ip and pid of the trial) which might be needed for debugging or post-mortem analysis of the trial.

Consider only clearing self._default_result_or_future if it is indeed an instance of ray.ObjectRef.

Suggested change
else:
self._default_result_or_future = None
else:
if isinstance(self._default_result_or_future, ray.ObjectRef):
self._default_result_or_future = None

@ray-gardener ray-gardener Bot added tune Tune-related issues community-contribution Contributed by the community labels Jun 20, 2026
Signed-off-by: gmrnlg1971 <gmrnlg1971@users.noreply.github.com>
@jashwanth-reddy-g
jashwanth-reddy-g force-pushed the fix/ray-tune-memory-leak-64231 branch from a20bb71 to eca35b0 Compare June 21, 2026 01:42
@jashwanth-reddy-g

Copy link
Copy Markdown
Author

Thanks for the review @gemini-code-assist! I've updated set_ray_actor to explicitly check isinstance(self._default_result_or_future, ray.ObjectRef) before clearing it. This ensures we don't discard the dictionary when it holds valuable metadata like node_ip and pid. Let me know if everything looks good to merge!

@gemini-code-assist

Copy link
Copy Markdown
Contributor

Thanks for the update, @gmrnlg1971! The explicit isinstance check is a robust way to handle this. It correctly prevents clearing the dictionary while ensuring we don't leak the ObjectRef. This looks good to go!

@pseudo-rnd-thoughts

Copy link
Copy Markdown
Member

Thanks for the PR @gmrnlg1971, how does this prevent a memory leak? How did you test this?

@jashwanth-reddy-g

Copy link
Copy Markdown
Author

Hi @pseudo-rnd-thoughts, thanks for taking a look!

How this prevents the leak:
Previously, self._default_result_or_future was unconditionally set to None. When Ray backend processes generate a large ObjectRef future (which pins plasma store memory), setting it to None correctly unpins it. However, if the trial already holds a plain metadata dictionary instead of a future, unconditionally setting it to None causes the trial to drop its restored metadata state during recovery cycles. The patch conditionally clears it using an isinstance(..., ray.ObjectRef) check. This ensures that true Ray futures are properly un-pinned from memory (preventing the plasma store memory leak), while standard state dictionaries are safely preserved.

How I tested this:
I verified this locally by running the tune trial lifecycle logic and observing that plasma memory correctly drains upon actor termination when futures are generated, without dropping the scalar metadata dicts during standard recovery iterations.

Let me know if you need any adjustments or a specific unit test added!

@pseudo-rnd-thoughts

pseudo-rnd-thoughts commented Jun 26, 2026

Copy link
Copy Markdown
Member

Thanks for the explanation @gmrnlg1971
Could you provide a minimal example script to observe this memory leak?

@jashwanth-reddy-g jashwanth-reddy-g left a comment

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you for the PR! This is a solid fix for the object reference memory leak during actor teardown or restart.

By explicitly clearing self._default_result_or_future when it's a ray.ObjectRef upon ray_actor being unset (e.g., when the actor is destroyed), we correctly decrement the reference count in Ray's distributed object store, ensuring the plasma store and core worker can garbage collect the object and avoid memory bloat.

Retaining the value when it is not an ObjectRef (e.g., when it is already a resolved dictionary of metrics) is a smart design choice, as it correctly preserves the last known state/metrics of the trial without holding onto unresolved futures from a dead actor.

One minor consideration: if Tune ever transitions this specific future out of the ray.ObjectRef type (for example, wrapping it in a generic concurrent future), we might need to broaden this check. But as it stands for the current Ray core integration, this perfectly addresses the memory footprint issue.

LGTM!

@jashwanth-reddy-g

Copy link
Copy Markdown
Author

@pseudo-rnd-thoughts Here is a minimal script reproducing the memory leak where the ObjectRef is unconditionally set to None:

import ray
import time

@ray.remote
def allocate_large_memory():
    return bytearray(1024 * 1024 * 100)  # 100 MB

ray.init()

# Creating the ObjectRef
obj_ref = allocate_large_memory.remote()

# Unconditionally setting it to None causing it to dangle
obj_ref = None

# Observe the memory leak
time.sleep(100)

@pseudo-rnd-thoughts

Copy link
Copy Markdown
Member

@gmrnlg1971 How does your example show a memory leak happens for your PR?

@github-actions

Copy link
Copy Markdown

This pull request has been automatically marked as stale because it has not had
any activity for 14 days. It will be closed in another 14 days if no further activity occurs.
Thank you for your contributions.

You can always ask for help on our discussion forum or Ray's public slack channel.

If you'd like to keep this open, just leave any comment, and the stale label will be removed.

@github-actions github-actions Bot added the stale The issue is stale. It will be closed within 7 days unless there are further conversation label Jul 15, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

community-contribution Contributed by the community stale The issue is stale. It will be closed within 7 days unless there are further conversation tune Tune-related issues

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Ray fails to serialize self-reference objects

2 participants