My talk from Swift Cloud Workshop 2 in Austin, TX on 2017-09-30 about the current state of memory management with Swift in the Cloud, and whether or not Swift on the Server is mature enough for your use-case.
Computer 10: Lesson 10 - Online Crimes and Hazards
Managing Memory in Swift (Yes, that's a thing)
1. Managing Memory in Swift
(Yes, that's a thing)
Swift Cloud Workshop 2 Austin, TX
30 September 2017
Illustration
Renders
by
https://pixabay.com/en/users/3dman_eu-1553824/
@CarlBrwn
2. Managing Memory in Swift
(Yes, that's a thing)
Swift Cloud Workshop 2 Austin, TX
30 September 2017
Illustration
Renders
by
https://pixabay.com/en/users/3dman_eu-1553824/
@CarlBrwn
3. Obligatory Bio
• Swift on the Server Developer at IBM
• First iOS App in 2008, many projects since
• Author, App Accomplished
• Meetup Organizer
• SwiftAustin & CocoaCoders
• Parent
@CarlBrwn
4. Is Swift on the Server READY?
• In my (personal) Opinion, [NOT speaking
on behalf of IBM], this answer depends
on two things:
1. Do you need the ecosystem to have
more features that you can build
yourself?
in the Cloud
[For your app?]
@CarlBrwn
5. Tale of Two Ecosystems
• NPM Claims 475,000 available node.js
packages.
• IBM’s Swift Package Catalog has 4000 entries
(last I looked), and that number includes at
least some iOS-only packages.
• So if you want to do Server-Side Swift, be
prepared to roll your own.
• Personally, I’m okay with writing my own
left-pad, but YMMV.
@CarlBrwn
6. Is Swift on the Server READY?
• In my (personal) Opinion, [NOT speaking
on behalf of IBM], this answer depends
on two things:
1. Do you need the ecosystem to have
more features that you can build
yourself?
2. Can you manage your own memory?
in the Cloud
[For your app?]
@CarlBrwn
9. Memory (6.4%)
• Most people I talk to don’t think of Swift
Memory Management as a problem
• Some of the Apple folks I talked to
about it at WWDC this year were
surprised, too (at first)
• It is a real problem, and it’s serious
• And it’s worse on Linux…
• For 3 primary reasons:
@CarlBrwn
10. Reason 1: Duration
• Cloud apps run longer (in general)
• Cloud apps can’t restart themselves in
the background while you’re checking
Facebook
• Memory leaks are cumulative
• Cloud costs scale with dedicated RAM
@CarlBrwn
11. Reason 2: Tools
• We don’t have Xcode
• We don’t have Instruments
• We don’t have cycle detectors
• In fact, we don’t have any Swift-aware
memory diagnostics on Linux at all
@CarlBrwn
12. Reason 3: Structure
• iOS (and Mac) Apps have a Structure:
• Application Delegate
• View Controllers
• Views
• Well-tested clean-up code
• (e.g. Views reclaimed when removed from
the screen)
• By contrast, Linux has: main
• Biggest problem w/Swift on Linux (IMNSHO)
@CarlBrwn
13. BRIEF Intro to ARC
• Automatic Reference Counting
• In the Old Days (2009), we would call
retain, release or autorelease on each
object by hand
• Apple published rules about when you were
supposed to use each
• Apple wrote an Analyzer tool that would tell
you when you broke the rules
• ARC does for you what those rules said to do
1/6
@CarlBrwn
14. BRIEF Intro to ARC
• Each object has a counter
• (Originally this was part of the NSObject implementation, but now
it applies to more things, but I’m probably still going to say “object”
today)
• When you take a reference to something (like
putting it in an ivar), you increment its counter
• When you’re done with it (or the system is done
with you), its counter is decremented
• The object only knows its count. It has no idea
who is pointing at it.
2/6
@CarlBrwn
15. BRIEF Intro to ARC
• ARC keeps things around as long as their
reference counts are greater than zero
• When a counter drops to zero, the object is
available to be reclaimed
• Reclamation happens periodically and
deterministically, but not necessarily
instantly
3/6
@CarlBrwn
16. BRIEF Intro to ARC
• When an object is reclaimed, all the things
it’s referencing have their counts reduced
by one
• That often causes those things to get
reclaimed in turn, and so on
• Unlike GC, no marking or sweeping is
needed during reclamation
• The system doesn’t need to pause the
whole system to free up memory
RAM
ARC
Object
4/6
@CarlBrwn
18. BRIEF Intro to ARC
• When two things refer to each other, ARC is
powerless
• Neither count will ever be set to zero
• The objects can never be reclaimed
• Some Garbage Collectors in Other Languages
can find and reclaim these kinds of cycles at
runtime (with a performance penalty), but in
Swift it’s not happening
• If no other objects can reach either of these, you
get a classic leak
6/6
@CarlBrwn
19. Good Structures
• Clear lines of ownership
• Clear relationships & hierarchies
• Tend to use ivars/properties
• Tend not to remember things passed in
from outside (especially from caller)
• Tend to reinforce existing relationships
& events
• e.g. Memory gets naturally
reclaimed when views leave the
screen or network connections drop
@CarlBrwn
20. Bad Structures
• Tangled or unclear relationships
• Strong references to things passed in
from outside
• Things captured in closures
• Hierarchies not rooted in natural
events
• e.g. Needs clean up
functions instead of it
“just happening”
• “Missed Dominos”
@CarlBrwn
21. You have to Measure
• This is a necessary step - you can’t reliably quantify
memory issues from reading code (if you can, you
should be mining bitcoin in your head)
• This has to happen at runtime, static analysis won’t
help
• You need either a production-like synthetic load, or
to measure in actual production (or both)
• Honestly, I don’t think many people do this (even for
iOS)
@CarlBrwn
22. My Measuring Scripts are Available
https://github.com/carlbrown/SwiftServerComparison
May or may not work for your use-case, but you’re welcome to them
@CarlBrwn
23. Wish there was a better way
Run long test,
grab `ps` logs,
graph
Rollback,
Next or
Fixed?
Test with `heaptrack`
to find next
Area of interest
Change code
@CarlBrwn
24. Graph of RSS from `ps aux`
Moving the slope, One little fix at a time
@CarlBrwn
25. Variability can be a Problem
Hard to see the leak (signal) when leak is small compared to the spread (noise)
This was happening because of clean-up functions
As you run the test longer, it becomes less of an issue@CarlBrwn
28. Wish there was a better way
Run long test,
grab `ps` logs,
graph
Rollback,
Next or
Fixed?
Test with `heaptrack`
to find next
Area of interest
@CarlBrwn
29. Darwin’s cool tools aren’t for you…
Nothing remotely like it on Linux
Even if you test on Darwin, there may still be Linux leaks
@CarlBrwn
30. …And that’s a real shame
Because the leaks that happen when you don’t have an
App Delegate structure can get REALLY convoluted
@CarlBrwn
31. Use heaptrack & heaptrack_gui
Not constrained to single thread like valgrind
But no awareness of Swift reference counts or structure
@CarlBrwn
32. Wish there was a better way
Run long test,
grab `ps` logs,
graph
Rollback,
Next or
Fixed?
Test with `heaptrack`
to find next
Area of interest
Change code
@CarlBrwn
33. So what code do you change?
• heaptrack only gives us the line of code that allocated the object
that is “stuck" in memory (& you may want to run swift-demangle)
• Find all the places that object is used and especially all the times it’s
passed to a closure or as an argument
• See if you can make them weak without crashing (or unowned, but
only if you have to - it’s not safe)
• See if making it weak at that point changes the slope of the graph
• Make only one change at a time, and measure
• Continue until you’ve found the right place(s) to make weak
• NOTE: This will only work if ownership is clear. If not, refactor
Good Question
@CarlBrwn
34. Most of my changes turn out to be dead-ends
The ones that make the slope better get merged to `master`
@CarlBrwn