managing memory in swift (yes, that's a thing)
TRANSCRIPT
Managing Memory in Swift (Yes, that's a thing)
Swift Cloud Workshop 2 Austin, TX30 September 2017
Illustration Renders
by https://pixabay.com/en/users/3dman_eu-1553824/
@CarlBrwn
Managing Memory in Swift (Yes, that's a thing)
Swift Cloud Workshop 2 Austin, TX30 September 2017
Illustration Renders
by https://pixabay.com/en/users/3dman_eu-1553824/
@CarlBrwn
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
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
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
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
Previously on “Conferences
with Carl”:
try!Swift NYC 2017• This talk expands on that talk• It should be up on video at
Realm sometime soon• Not necessary to have seen it
@CarlBrwn
0
35
70
105
140
PR Fixes
CodeOrdering Counting Encapsulation Logic(App) MemoryNaming Optionals Performance Threading TypingUnclear
PRs Meeting Criteria (502 total)
?
Today’s Talk
@CarlBrwn
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
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
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
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
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
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
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
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
RAMARC
Object
4/6
@CarlBrwn
ARC-Optimized ArchitectureThis is the world ARC wants (and expects)
5/6
BRIEF Intro to ARC
@CarlBrwn
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
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
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
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
My Measuring Scripts are Availablehttps://github.com/carlbrown/SwiftServerComparison
May or may not work for your use-case, but you’re welcome to them@CarlBrwn
Wish there was a better wayRun long test, grab `ps` logs,
graphRollback, Next or Fixed?
Test with `heaptrack` to find next
Area of interest
Change code
@CarlBrwn
Graph of RSS from `ps aux`Moving the slope, One little fix at a time
@CarlBrwn
Variability can be a ProblemHard to see the leak (signal) when leak is small compared to the spread (noise)
This was happening because of clean-up functionsAs you run the test longer, it becomes less of an issue@CarlBrwn
DurationThis is a half hour run
@CarlBrwn
Duration (cont)Same run, full duration (~5 hours)
@CarlBrwn
Wish there was a better wayRun long test, grab `ps` logs,
graphRollback, Next or Fixed?
Test with `heaptrack` to find next
Area of interest @CarlBrwn
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
…And that’s a real shameBecause the leaks that happen when you don’t have an
App Delegate structure can get REALLY convoluted@CarlBrwn
Use heaptrack & heaptrack_guiNot constrained to single thread like valgrind
But no awareness of Swift reference counts or structure@CarlBrwn
Wish there was a better wayRun long test, grab `ps` logs,
graphRollback, Next or Fixed?
Test with `heaptrack` to find next
Area of interest
Change code
@CarlBrwn
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
Most of my changes turn out to be dead-endsThe ones that make the slope better get merged to `master`
@CarlBrwn
Thank You& Good Luck
@CarlBrwn
Thank You& Good Luck
@CarlBrwn