-
Notifications
You must be signed in to change notification settings - Fork 3.5k
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
Fix xterm.js resource leaks in split pane #3409
Conversation
@igorsdv this is absolutely amazing. Thank you so much! |
lib/components/term.js
Outdated
// Release listeners and disposables | ||
_core._disposables.forEach(d => d.dispose()); | ||
_core._disposables.length = 0; | ||
_core._events = {}; |
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.
Not all listeners are installed when open is called, doing this on multiple objects will almost certainly break other components. Can the code be changed to call open
once? I think it works fine if you open on some parent element and transfer that between split elements.
Also here is upstream request xtermjs/xterm.js#1323
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.
@Tyriar Thanks! The only listener not registered in open
that I noticed was InputHandler. In the end the object ends up with the same number of listeners as before the dispose
calls. But yes I agree that calling open
once and reattaching the container as you suggest would be better, not sure how much more complicated it is with React but it should be possible.
Implemented @Tyriar's suggestion to reattach the xterm.js parent element without calling |
Awesome 🎉 Thanks for your assist @Tyriar |
Removing It has broken some plugins: chabou/hyper-pane#42 |
Fixes #3408, fixes #3351, fixes #3281.
Repeated calls to
Terminal.open()
on the same instance register a number of event listeners each time, which leads to resource leaks and erroneous listener duplication. It's not clear to me if multiple calls toTerminal.open()
is a use case supported by xterm.js -- if it is, this issue should be fixed there. In the meantime, this is an ugly workaround to release all the event listeners prior to callingTerminal.open()
again, while preserving the state we need to keep.First, we run the equivalent of
Terminal.dispose()
to release disposables and events on theTerminal
object. After this, some event listeners on non-disposable properties of theTerminal
object itself remain set, and we must handle those separately. This is important because the old event listeners indirectly close over the previous_parent
elements (now detached), which hold references to unmounted React components and old props, which in turn refer to oldTerminal
objects representing closed panes and tabs. Thus a single unreleased listener can be enough to cause a significant resource leak.This can be seen using the Developer Tools by going to Memory > Take Snapshot. Multiple non-GCed
Term
components andTerminal
instances are reachable even when only one pane remains open.Additionally, the necessary cleanup on
TERM_GROUP_EXIT
inlib/components/terms.js
is moved fromonRef()
tocomponentWillReceiveProps()
. This is becausethis.props.sessions
is not yet updated whenonRef()
is called, and currently thedelete
statement is never executed.