SwiftWebSocket icon indicating copy to clipboard operation
SwiftWebSocket copied to clipboard

Memory Usage when sending a large message

Open jshafer817 opened this issue 4 years ago • 4 comments

Hi, it seems that if I send a large string to a different socket server, after sending the message, the ram is never released. The string is 5MB.

Works fine, but.. even after the message is send, and closed.. ram is not released.

ws.send (text: self.jpegPhotoData!.base64EncodedString(options: .lineLength64Characters))

Any ideas?

jshafer817 avatar Sep 04 '19 05:09 jshafer817

@jshafer817, I noticed this library creates some nasty retain cycles by virtue of the functional interface design of the open, close, error, and message methods on event. I did not see an obvious way to provide a weak reference to the library for those callbacks and decided the best way to stop the bleeding was to wrap the Websocket object in another object that is allocated only once. I hope the memory issues get fixed in future builds, but expect it'll require a significant change in how we interface with the library.

gacrichards avatar Sep 04 '19 16:09 gacrichards

"wrap the Websocket object in another object that is allocated only once".. Do you have an example you are willing to share? Thanks for the clues.

jshafer817 avatar Sep 04 '19 19:09 jshafer817

This is my wrapper object (https://gist.github.com/gacrichards/e652ac7281218dffbc5b72ce1b08c898). The basic idea here is that I take the functional bindings of a Websocket() instance and map them to a weakly held delegate owned by my wrapper object. I only ever want to create one instance of my wrapper object since it is in a strong reference cycle with it's websocket object - releasing my wrapper will cause a leak. In many of my iOS apps I have a dependency factory which is a singleton and holds on to core objects (as few as possible) that need to be injected throughout the app. I place my wrapper object in the dependency factory and that gives me pretty tight control on its life cycle. Here is an example of what that might look like (https://gist.github.com/gacrichards/fb714b34ea5304729b8fae26fec23196). The only state I care about for my application is the request that I use to open the websocket since some websocket connections need to be authorized with an access token. I use my wrapper object add authorization or revoke authorization from that request, i.e. to mutate any state. Fortunately the socket doesn't appear to hold a strong reference to the request so I'm free to change that as I need. The obvious drawback to this approach is that I'll always have a websocket and wrapper object in memory, but that is better, IMHO, than leaking the socket and any other objects that it comes into contact with. If I've gone off in a weird and unfruitful direction I'd love to be corrected, but this passes memory profiling tests for my app.

gacrichards avatar Sep 04 '19 20:09 gacrichards

@jshafer817 , thanks, do you mean you find that

  • sending ordinary small messages it is released

  • but only "large" messages are not released

?

It seems strange there would be any difference, you know? What do you think?

smhk avatar Sep 09 '19 10:09 smhk