Exploring Swift Memory Layout

Mike Ash

Recorded at GOTO 2016

Get notified about Mike Ash

Sign up to a email when Mike Ash publishes a new video

so we're gonna be talking about memory
layout in Swift as I'm sure you know
Swift is Apple's brand new magical fancy
programming language and I'm gonna go
dive into it a little bit and talk about
the bits and bytes and how it's all put
together and what stuff looks like in in
memory when you actually run the code on
a computer real brief about me I'm on
line at my cache comm I have a blog
where I do all sorts of crazy stuff like
this I take like to take things apart
and see how they tick and I've got a
bunch of crazy github projects which you
should probably never use for anything
but are lots of fun to play with I'm on
Twitter if you feel like following me
there's a picture of my cat because you
know the Internet is all about cats and
we're fundamentally all about the
Internet these days I fly gliders just
point of information it's a lot of fun
and the arrow is kind of pointing over
here so that's me
you know I always put a photo of myself
on these slides and then laughter words
and like people can just look at me so I
decided I'd stop doing that so here's
the plan first I just want to give a
quick overview of what memory is I'm
sure you all know what memory is but it
can help to get a little bit of
perspective and just tear it down you
now go to the foundation's revisit the
fundamental stuff then I wrote a program
that basically is where I generated all
this information it actually goes
through crawls the program starting from
a particular value and dumps everything
out that are in those values in memory
and then finally I'm gonna actually dive
into how swift lays out stuff in memory
how you know what that program actually
produces and some contrast with how see
does it and how C++ does it so what is
memory and fundamentally memory is what
stops this from happening so you know
you got to keep track of where you are
essentially you've got a computational
process and you are at some state within
that process at all times and if you
can't keep track of that then you will
just never get anywhere so we don't want
to just endlessly repeat we want to
actually make progress
and that's what this is all about so
figuring out how to actually build
hardware which can remember things and
you know store information and dig it
out later was kind of one of the
fundamental problems in computing and
there's lots of technologies along the
way started out with vacuum tubes you
can imagine these things are like this
big and they're essentially like an
incandescent light bulb and each one
holds one bit so if you want to actually
store some reasonable amount of data
you're talking about a roomful of
incredibly hot equipment later on there
were mercury delay lines this is kind of
a cool technology of a pipe you
basically fill it with mercury you have
a speaker or something like that on one
end and something like a microphone on
the other end and you pulse your data
through it and it takes time to travel
and because of that you could fit stuff
in and store your information that way
and there was a fun little proposal
somebody decided that gin would make a
good medium for this had all the right
chemical properties and whatever as far
as I can tell nobody ever built that but
fun little aside magnetic core memory
was an advancement of this stuff it was
very neat technology get little rings of
cup of iron and you run wires through
them and depending on the electrical
current you send through them you can
store data or retrieve data by storing
it in the magnetic field in that in
those rings and so that was one ring per
bit and the state of the art of this in
the 60s or 70s was basically a cube
about this big could hold 32 thousand
bits of information and then you can
imagine you know this thing I've got in
my hand can hold many gigabytes of
memory billions of bits and so things
have advanced a lot since then
so DRAM dynamic Ram basically silicon
chips is the state of the art today
which we should all be incredibly
thankful for because it really makes our
lives a lot easier the fact that we can
have these you know this allows us to
store billions and billions of bits of
information all at once and my phone is
misbehaving if you can pardon me for
just a moment here there we go all right
so that's the hardware view of things we
don't really care too much about
Hardware most of the time
programming because that all the
analogous works we ignore it so how does
it look for a programmer so we got the
the fundamental unit of information is
traditionally we organize bits and
groups of eight those are bytes and each
we memory is essentially just a long
sequence of bytes one after the other
just wandering you know heading off into
the mist and they're arranged in a
certain order every bite gets a number
that's called its address so we start at
zero and then one and then two and then
three and an eight billion byte system
we've got you know billions off in the
distance it can be you know you can view
these things in different directions
often we view it like this organized by
word instead of by byte so a word is a
vague term of the art in computer
science but usually it means a unit
that's the size of a pointer so on
modern devices at 64 bits or 8 bytes and
it just heads off to infinity so here
I've got the bytes addressed by 8 and
we'd like hexadecimal hexadecimal is
where you got base 16 addressing instead
of base 8 so 0 through 9 then a through
F that's a nice multiple of 2 so
everything fits together nicely it's the
kind of a native language of computing
so it's the natural language to use here
and so I've got the addresses done in
hexadecimal instead 0 816 is 1024 and
all that and this is just the big
picture of what this whole thing looks
like if you zoomed out this is a Mac
running on x86 64 everything's a little
bit different in this all very platform
specific but essentially you've got a
gap of stuff that doesn't exist the
first 4 gigabytes of memory is not
mapped this is all this doesn't take up
any physical space it's just a an
addressing trick then you've got the
user your program essentially your
memory is the green stuff
so you get a chunk that's for you and
then you've got a nice big empty space
after that and then finally the kernel
is down at the bottom so you've got this
2 to the 64th power bytes which get
sliced up and organized like this and
this is essentially how it looks if you
zoom in a little bit so this is the same
picture as before except it's more
realistic because instead of starting at
0 we're starting at 4 billion we've got
pointers in memory I'm sure you're all
familiar with the term pointer
references a pointer at this level is
just a number and it's a number that
address of something else in memory so
here we've got this thing up there
that's the stores the address of this
bit down there and I just indicate that
with an arrow the arrow doesn't exist in
reality it's just a number that we treat
as if it were an arrow and one more
detail on all of this whoops
went too far we in the most modern
systems store things in little-endian
order which is essentially the least
significant part of the number comes
first so it's as if you were out a you
know 234 as 4 3 2 everything's backwards
just one of those things you just kind
of have to learn to live with it and
read it that way
so memory is organized into as far as we
see it is organized into three
fundamental parts we've got at the
hardware level it's just a big list of
stuff but as the way we actually treat
it the parts of it have more specific
purposes so we've got the stack which is
where all your local variables go when
you write variables that you're using in
your computations in a function that
goes on the stack and it's called a
stack because every time you make a
function call it adds that functions
local variables to it and when you make
another call it goes up and another call
goes up and then when you return from a
function that goes back down back down
back down so it's like a stack of things
you've also got global data in your
program those are essentially things
that came along as part of the program
when you loaded it so your glow
variables are part of that your string
constants your type metadata in swift in
in other languages just gets loaded as
part of that and then you've got the
heap and the heap is dynamically
allocated stuff when you create a new
object that's allocate some memory
that's on the heap and these are things
where basically they're not they don't
live permanently they've got some
lifetime but they're not tied to a
function they're not tied to your
program they they come to life and go
away kind of arbitrarily and that's
essentially everything else when you
create a new object when you allocate
memory manually when you concatenate
strings whatever that's all on the heap
so that's kind of the overview you know
remind you what memory is what the whole
deal is that we're talking about here
let's get into dumping memory actually
going diving in inspecting the contents
seeing what's actually in there so I'm
going to explore this program that I
wrote how it works that actually goes in
and traces all this stuff out if anyone
wants to take a look at it I've got it
up on github the github address is there
or there's a tiny URL below or if anyone
likes really huge URLs you can use that
one at the bottom but I'm not gonna wait
for you to type it out
just real quick this eases Xcode eight
and Swift three if anyone's doing so
anything with Swift so far you know that
there have been a lot of versions of it
and they like to break compatibility so
last year was Swift to starting a couple
of weeks ago we've got Swift three now
and that's different so if you want to
use that code you need this so back to
this the kind of fundamental unit of
like this and this is a function that
works on an arbitrary type it takes a
value and it's going to return an array
of unsigned 8-bit integers or bytes and
we'll just use it as in the demo above
you create a variable containing
something any arbitrary thing and then
you just call this and it'll hand you
back the bytes that make it up and
that's going to serve as the foundation
for this whole program and the question
is how do we write this in
specifically this is a real quick
overview of one possible implementation
which is not what the program actually
uses but it kind of gets the whole the
the ball rolling as far as how this
works so the idea is you get the value
you get its size because we know the
type of the value this is a generic
function in Swift so it works on any
type but it knows what type it's being
called with at any given time this
memory layout type allows us to get the
actual size so that tells us how many
bytes it is so we know how long it is in
memory and then there is this built-in
function in Swift called with unsafe
pointer so you call that you give it a
variable and it comes back and gives you
a pointer to that variable and once we
have a pointer we can do things like
look at that pointer as if it were a
pointer of a different type so we
imagine you have a pointer to an int and
we do this with memory rebound call here
which says okay pretend that this is not
a pointer to an integer pretend that
this is a pointer to bytes and just work
with me on this you know it's the same
thing but it's a different type now go
through and read it so what this does
this takes a pointer to whatever
arbitrary thing you've got and says all
right just pretend it's raw bytes
interpret it that way
and then once we have that we can go
through and just read one by one and
that's a bit of a shortcut here I just
tell the system to read it for me
there's no loop or anything like that
unsafe buffer pointer basically lets me
say it's a container and then I can
create an array from that and I'll just
kind of happened Swift let's let's you
write short stuff like that so a real
quick demo of what this produces I
created a variable that contains 1 2 3 4
5 6 7 8 and then I just dumped out 42
and if I print these things then I get
these results here so you can see the
first one prints out exactly what we saw
except it's backwards because remember
modern systems tend to do things
backwards so it prints out eight seven
six five four three two one zero and
then 42 comes out as 42 with a bunch of
zeros after it because it's a 64 byte
before bit integer and just real quick
you don't need to actually follow this
this is just you know some code I wrote
that I wanted to put up real quick
hexadecimal is again the natural
language of low-level computing so this
just takes an array of bytes and dumps
it out as a hexadecimal string instead
of as a sequence of decimal integers
like we saw before so if we use that
then we get this instead same basic
thing except instead of printing decimal
we get hex so one two three four five
six seven eight comes out just as it did
before and then 42 comes out as 2a since
that's what 42 is in hexadecimal
followed by a bunch of zeros so this
lets us dump the stuff out in a form
that we can understand but it's still
close to what the computer has all right
so we got this where we can take a value
and see what's in it but real programs
are more complicated than single values
right they've got a lot more going on
real programs look more like this okay
you've got a value which contains a
bunch of bytes and those bytes can some
of those bytes are actually pointers
which point other stuff and those point
to other things and you know you get
this whole tree thing going on so we
want to be able to actually chase all
this stuff down in an automated fashion
the program needs to be able to actually
find all of this stuff so how do we do
that we start off with knowledge that
pointers are just integers all right a
pointer is just an address it's just a
number which we interpret as another
location in memory so I wrote a quick
struct which gets used in the program
all it is is it just contains an address
which is an unsigned integer pointer
sized integer and wrapping it in a
struct helps me keep things apart so you
don't confuse which parameters are
actually integers and which parameters
are integers which we are treating as
pointers right just make a new type so
that the type system helps us write
program correctly and then this bit of
code essentially takes an array of bytes
which we already know how to obtain we
just wrote that function and it takes
that array of bytes and tries to scan
for pointers in it so again a pointer is
an integer that you happen to treat as
is being treated at this level because
we just get a bunch of lights and we
don't know what they mean so we're just
going to kind of optimistically go
through and slice it up into chunks of
8-bit eight bytes and pull them all out
and pretend say you know what if these
were pointers what would that mean so
that's what this does we take this array
of bytes and we say instead of taking
treating it as an array of bytes
give me a pointer to its contents and
treat it as a pointer to pointers okay
which means that we can essentially go
through and say read the first pointer
sized chunk read the second pointer
chunk and then we take all of that and
return it as an array so we this code
essentially will just go through this
big array of bytes that we get from the
thing before and divide it up which like
this so this is a visual indication of
what's going on with that code so we
take give it a value it returns a bunch
of bytes and we go through slice it up
and get the individual pieces and then
we can start chasing those down so we
can read a value grab all of its bytes
then we can grab all the pointers that
those bytes might indicate and then we
can take those pointers and repeat the
process and essentially that gives you
the tree you kissed in a loop keep going
through as long as you've got pointers
to explore you read their contents and
then you spit them out the problem with
this approach we don't know which
pointers are actually pointers and which
might be the players high score it might
be you know the number of people who
dislike you or something like that and
we don't know what they mean and
normally in a program when you try to
read from a pointer that's not actually
a pointer it just is some illegal piece
of memory than your program crashes
which is good in normal code because you
don't want to proceed when your program
is that confused you want it to just
stop and produce a crash log or
something like that but in this code we
want to be able to keep going so we can
explore this stuff so we want to be able
to read from pointers without crash
if they're bad so on the Mac and on iOS
we've got this nice low-level function
Apple platforms use a mock kernels
highly modified and added onto stuff but
the low-level mock calls are still there
and there's a mock call called mach vm
read overwrite and essentially it's a
system call where you give it two
pointers and you say I want to copy this
many bytes from that pointer to this
pointer if you're familiar with the mem
copy function from the C standard
library it's exactly like that except
that if you give mem copy a bad point or
your program crashes and if you give
mach vm read / write a bad pointer it's
okay it returns an error because it's a
system call it happens that the kernel
level the kernel level can do all this
checking safely and so it can come back
and say I couldn't do that because you
know that is not a real pointer that was
just a bunch of junk and it doesn't you
know the address there doesn't exist and
so based on that we can go through and
reliably follow this tree without
crashing because we can essentially
optimistically try every pointer pass it
to this function and then if it comes
back and says there was an error that's
fine we just say okay couldn't follow
that keep on going this is a real quick
this is just the function prototype what
it looks like it takes a task which is
like a process if you got the right
permissions on the Mac you can actually
read from other processes not your own
which is sort of the foundations of how
you can build a debugger it takes an
address it takes a length it takes a
destination address and it takes a
pointer to something where it will tell
you how many bytes that actually read so
back at the beginning I showed a
function that would read from a pointer
but it was it would crash if you gave it
a bad pointer this will read from a
pointer safely so essentially it's just
a wrapper around that mock call it takes
the pointer you give it it does a little
bit of casting to get it into the form
that the system wants and then it just
makes that call if it succeeds it
returns and says hey we did it and if it
doesn't then it returns false the caller
can know that it didn't work and so that
way we based on this we can build this
there we go all right so we can read
this stuff safely but we need to know
how much to read the first value we read
we can get the size of the type because
generic function we get that metadata
from the compiler but after we start
chasing pointers we can't do that
anymore because we're dealing with
arbitrary bags of bytes we don't know
what this stuff is so we need to know we
need to be able to at least guess how
many bytes to read at any given time and
when you chase these pointers through
for stuff that's on the heap
there's the malloc size function at
least on the Mac and on iOS where you
give it a pointer and it comes back and
says there were actually you know 32
bytes allocated on the heap here so we
can call that and it comes back and
tells us exactly how much we can read
which is great and even better this
function is tolerant of bad data so if
you give it something that's not a
pointer or you give it two pointer to
something that's legitimate but not
allocated on the heap or you give it a
pointer to something in the middle of
something else whatever it doesn't care
it will give you back zero so it doesn't
crash which is really convenient for our
purposes and finally we've got global
variables code things like that those
are symbols in your app there's the DL
adder function where you give it an
address and it comes back and tells you
what symbol is nearby and so we can use
that to check to see if something is
actually a symbol and we can also use it
to kind of extract the size by
essentially scanning it gives you the
symbol that comes immediately before the
pointer you give it so you start from
here and you say give me the symbol
information and if it comes back and
says yes I have symbol information then
advance it by one byte and say how about
here how about here how about here and
just keep doing that until it gives you
a different answer and then you know
exactly how long that thing was and as a
bonus it also gives you the names so
your function names your global variable
names things like that does all pop out
of this API and so we can use them to
annotate our scan and help us understand
what's going on those names in Swift and
also in C++ tend to come out mangled
because the compiler tries to embed
information about what the type is
besides just the name so in C for
example if you have a function called
summon you know it'll the symbol name
that it spits out just says summon and
in Swift if you have a function called
summon the symbol name comes out more
like this where you've got a bunch of
extra stuff on it because it will not
only include that name but it will also
include the fact that it takes two
integers and returns a string or
whatever it actually is so in order to
help with that there's the Swift D
mangle command that comes with Xcode I
imagine it's available in the Swift
open-source tools as well you give it a
mangled symbol and it comes back with
something like this which is more
readable so in my code I just dump
everything through that Swift d mangle
is a very nice program because if you
give it something it doesn't understand
it just gives it back to you unmodified
so I could just feed everything through
it without having to fear that it would
explode or crash or something like that
on data that wasn't actually mangled
Swift symbols and then C++ has the same
thing there's a tool called C++ filt
which does the same job for C++ names
and it has the same semantics or if you
give it something it doesn't understand
it gives it back to you without changing
lot of this data that we come across in
memories actually strings
write textual information like method
names like user input and it's useful to
be able to find these and the trouble is
again we're working with these bags of
bytes we don't know what's going on with
them they're just a sequence of data and
we want to be able to at least guess at
which sequences of data actually
represent text and which don't and
there's no way to do this reliably but a
decent heuristic is to look for ASCII
characters and look for printable ASCII
control characters which we are we don't
expect to find as part of text in a
at least not the text that we're
interested in and then stuff beyond 126
is either the delete character and ASCII
or its non ASCII characters so we look
for printable ASCII characters and we
look for sequences of at least four so
the idea is that if you just have one or
two or three then it's likely that's
just some other data that just
coincidentally happened to look like
text and once you get up to four there's
a decent chance that it's something
texturally interesting and it's not a
guarantee but it's it's a decent
heuristic it gives you good results and
this is code that just goes through and
implements that heuristic here you give
it an array of bytes and it goes through
its splits that array into chunks of
contiguous printable ASCII characters
and then filters out all the short ones
can run this on the byte arrays that we
get out of the scanner to see what's
going on in them all right so this is
that's essentially those are the
foundations of the program there's a
bunch of bookkeeping that goes on in it
if you're interested in that part look
it up on github but those those are the
fundamental pieces and we now know how
to build all of that and so we can read
all this stuff but we want to be able to
actually output it in some form that's
nice for the human to look at so we
could just dump it all in text form or
something like that but it's going to
take a lot of work to interpret and we
ideally would want something more like
this and as an intermediate step I
produce something like this which is not
very readable at all but this is an open
source program called graphviz and you
give it essentially you give it a list
of nodes and you give it a list of
connections and you say this node has
this label and it's connected to these
nodes and this node has this label and
it's connected to these nodes and when
you hand it over to that program and
hands you back stuff like this which is
really cool and readable and you can go
through and look this is I wrote a
simple C program that creates a little
structure in memory and then hands it
off to my dumper program and that
generates the graphviz stuff and then
graph is turns it into a PDF which looks
like this so we can go through we can
read we can see we started off with a
up at the top that pointer points to
some malloc memory which contains this
and those point to more malloc memory
which point to more malloc memory and
we've got a couple of strings at the
bottom and we can go through to see this
whole structure visually which is cool
so that helps us figure out what's going
on so that's the theory of what's a how
we're looking at these things so let's
actually go through and look at them and
see what's going on with with this stuff
how does Swift represent things in
memory how does C represent remember
things in memory how does c++ represent
architectures specific I did this stuff
on Mac on x86 64 iOS 64-bit is likely to
be very similar Swift on Linux 64-bit is
likely to be similar but this is stuff
that's very useful for debugging it's
very useful for understanding how the
system works it is not a good idea to
write any code that relies on this stuff
unless it's kind of a hobby project or
an experimental thing you don't want to
write any production code that relies on
this stuff because offset sizes the
meaning of various fields is all subject
to change from one roof for one release
but you don't want to incorporate this
into you know that library that you're
writing for work that's going to ship to
users my phone is not cooperating with
me today there we go alright let's take
a look at some c structs c is very
simple in how it lays things out in
memory that's kind of its appeal and
we'll take a look at this real quick I
make AC struct which just contains three
long fields XY and Z I wrote a little
bit of code that fills them with one two
and three and then I dumped out that
memory using my nice graphical dumper
and that's what we get up here in the
bowl and you can see that it essentially
just lays them out sequentially we got 1
followed by 2 followed by 3 and there's
a bunch of empty space because long is
an 8-byte value and these are small
numbers so they have a lot of leaving
zeros and just puts them out one by one
it gets more interesting when you get
different sizes so this is a struct that
has a bunch of fields of different sizes
a through H some of them are one bytes
its character some of them are two bytes
those are short some of them are four
that's integers and some of them are
eight bytes that's long and again the
compiler just lays them out one by one
you can see one two three four five six
seven eight but if you look closely
you'll see that some of them take up
more space than they maybe ought to
number three for example three is one
byte is corresponds to C that's a one
got three followed by zero followed by
four so there's an extra space in there
the reason for that is that struct
fields get padded the idea is that it's
more efficient to access data when it's
on a memory address which is divisible
by its size or at least which is
divisible by whatever the hardware
architecture likes for it to have
typically it's its size so a two byte
value wants to be on an even-numbered
address a four byte value wants to be on
an address divisible by four and what
the compiler does it will waste this
essentially wastes memory but it trades
off memory against time by expanding
these fields a little bit adding some
space between them when necessary to
make sure they all line up nicely so
that they're fast to access and that's
really it for C C kind of has trucks and
that's about it and it just lays things
out sequentially and there's there's no
metadata there's no you know implicit
pointers or anything like that see what
you see is what you get
C++ gets more interesting though here's
a simple C++ class I've got three
virtual methods on it it's got one field
I create one and initialize it and dump
it out and this is what I get so we can
see now it's not just one bubble it's
got a bunch of different stuff and I'll
zoom it in so we can actually see what's
going on here up at the top is the
actual object that's the thing I created
which contains in its single field it
contains the value one and we can see
that it's got more than that so it just
had one field but here we've got more
stuff at the top
the program explored this and found that
at that thing at the top was a pointer
which points here and then that points
to more stuff and so that thing at the
top is a vtable pointer so in C++ the
way you do virtual method dispatch is
the first pointer sized chunk of an
object is a pointer to a V table which
is a table of function pointers so when
you call through to something like you
know object dot X what it actually does
is it uses that table to look up the
implementation of X for that object and
that's how inheritance is implemented if
you subclass something an override then
that generates a new table and that new
table contains new entries for those
method implementations so that the code
knows what it needs to call so here's an
example of that
quick C++ subclass it inherits from the
previous one it adds a new field it adds
a couple of new methods and when you
dump that out you get a little more
stuff and again I'll zoom in so here
we've got the object at the top like
before you've got this V table pointer
and then you got the fields and if
you'll remember field number one was
from the superclass field number two is
sequentially so the idea is that when
the superclass is doing stuff it can
look at it and it sees what it thinks is
itself and then the subclass data gets
laid out afterwards so there's no
conflict there but they're just
efficiently packed in memory just the
same and then the vert the table for the
subclass gets longer because there were
five methods now we had three from the
superclass two from the subclass and
then it just lists them sequentially so
every method just gets an index in this
table and subclasses get the same table
it's a superclass except they can be
potentially longer if they're more
methods added and entries get replaced
to indicate overriding let's take a look
at multiple inheritance this is where
things get interesting
C++ allows a class to subclass multiple
classes simultaneously so here is a
second superclass to go along with our
original and here is a subclass of both
so each superclass has some methods each
glass has a field the sub class has a
field create it fill it out with some
data and this is what we get it's a
little bit more complicated the good
news is that most of that is runtime
type information stuff that we can kind
of you know not look at too hard let's
zoom in and see what's going on
so again object is at the top and we can
see that it starts out similar so it's
got a V table pointer followed by that
first field which is one but then
something interesting happens instead of
doing just one two three laying out all
the fields sequentially we get another V
table pointer right in the middle of the
object and so this is how si implements
multiple inheritance we've got one V
table pointer at the top we've got
another one over there and the idea is
that it's kind of like two objects glued
so if you take this first one here
that's the V table that indicates it's
an instance of that first superclass and
then the second superclass gets laid out
below it and what happens normally in Si
and with simple C++ classes if you cache
between types you've got an instance of
a subclass you say treat this as if it
were an instance of its superclass this
is just like some bookkeeping trickery
right you've got the exact same pointer
and you just say okay pretend that this
means something else but when you get
multiple inheritance involved suddenly
things get a little more complicated and
if you say take this pointer and
interpret it as a pointer to its
superclass what it will actually do is
it will move that pointer a little bit
so in this case it's going to add 16 to
that address and give you a pointer into
the middle of this object and because
that vtable is right there it all just
kind of works out and it's a bit of a
crazy system but it gets the job done
and so you can see the effect here where
you've got essentially the v table for
both class for the subclass and you each
part of this object got two v tables on
the object each one points to a
different part of this feed table and
everything just kind of lines up with
these multiple super classes so it all
just works out lots of compile-time
trickery and then the end effect is that
runtime everything is nicely laid out
friendly and quick
that's usually okay so that's C++ you
get crazy stuff with multiple
inheritance but it's usually
straightforward again you get that V
table at the top which tells you what
kind of object it is and then every all
the fields are just laid out sometimes
you get padding depending on their sizes
but it's just one after the other after
the other is in line so let's move on to
Swift now and Swift starts out very
simple like C and like C++ so just to
get the ball rolling and created an
empty struct and you'll never guess what
it looks like an empty struct contains
nothing at all it's a zero sized object
interesting feature of this it does
still have an address in memory even
though it doesn't contain anything the
compiler still gives it an address which
I thought was kind of funny it probably
doesn't make a whole lot of sense for
the compiler to optimize for zero size
struct since we don't use those very
much move on to a more realistic example
more useful example here's a struct with
three fields this is essentially the
swift equivalent of that see example I
did at the beginning three fields one
two and three and it looks this is the
result the output the way it's laid out
in memory looks a lot like the way it
was laid out in memory and see and in
fact it doesn't just look like it is
exactly the same these two are laid out
in exactly the same way so Swift is just
laying it out one two three in a row
like that there's no fancy metadata
going on there's no extra stuff it's
just your fields and then I did the same
thing that I did before with the
multiple sizes and again we get the
exact same results so this is a
complicated struct with different fields
of different sizes and the output is
exactly the same as it was in C with one
exception you'll notice that after three
we get one two three and then there's
this 5f thing before four that's just
because the padding that gets inserted
does not have to contain any particular
value because it doesn't mean anything
the padding is ignored so before when I
ran the thing on C it just happened to
zero and when I ran it on this it just
happened to contain five F so this is
it's kind of like the junk DNA inside
your program but again it's just laid
out exactly the same way C is so there's
no overhead it's very straightforward
let's look at a little more complicated
thing let's see how a swift clasp looks
so simple thing complicated result it's
not as bad as it looks essentially what
you're getting in there is that Swift
has this whole hierarchy of stuff and it
represents it knows what types mean at
runtime and want to zoom in a little bit
so we can see the object but what'll all
this other stuff is going on is
essentially it's saying that your class
is actually a subclass of this hidden
Swift object class and in that class has
a meta class and all that stuff so
there's all this metadata that's going
on that you can use to inspect objects
and things like that but we can mostly
ignore it
so if we ignore all that other stuff and
kind of zoom in we look at the instance
of the object here and we can see the
data laid out in memory one two three
and there's a header above it which is
similar to the way that the way C++ was
in C++ we had a V table and then there
were the fields and in Swift we have an
is a pointer which is essentially the
swift equivalent of a V table it points
to the objects class then we've got some
other stuff which I'll talk about in a
moment and then you got the fields so
you got the same arrangement of a header
followed by the fields just packed in
memory nice linear fast Hardware
friendly and let's take a look at a
little bit more complicated class this
is the class equivalent of that struct
that I showed and it ends up being the
exact same thing with that sort of
header put at the top so you've got that
is a pointer you've got this other stuff
which I'll get to and then all the
fields are just laid out the exact same
way they would be in a struct
sequentially with some padding to make
everything line up nicely so this is
sort of the visual representation the
abstract representation because those
hexadecimal things get you know painful
to read after a while so this is what
they mean if you actually go in and
interpret it you've got the is a pointer
that other header field that I didn't
mention yet those are retain counts you
may or may not know swift operates using
automatic reference counting so it needs
to count the number of references to
each object and in Swift those counts
are stored in the object itself as that
second header field and then your stored
properties just get laid out after that
the compiler just put some one by one
and I did say retain counts plural so
there are actually two counts in a swift
object this is an interesting little
feature the way the system works there's
the strong count and the weak count so
when you make a normal reference to a
swift object that increments the strong
count and then if you make a weak
reference to an object that increments
the weak count and the idea is that when
weak count is nonzero then the object is
destroyed but it's not de-allocated and
that could be a talk by itself
I got a blog article about it if anyone
cares about exactly how that works but
that's essentially what we're seeing
there so there are two separate counts
packed into the same field each one I
think it's like 31 bits or something
like that
and then let's look at that is a
structure so that is a structure in C++
the V table was just a list of method
pointers and Swift it's a little bit
more complicated partly because of
objective-c Interop Swift has to work
with apple's objective-c stuff and in
fact all Swift classes in memory are
also objective-c classes as fact is
hidden from us sometimes if you
explicitly subclass and reject a c class
then you can see it if you use the at
table C annotation you can see it but
even if you do none of that and you do
what looks like a pure Swift class it's
actually an objective-c class just the
same and just to be a little bit more
accurate the first part of an object is
not necessarily the is a pointer
sometimes it's the is a pointer along
with some other junk this is just a way
to sort of efficiently pack some
metadata in there Apple does this on iOS
64-bit I don't believe they do this in
the Mac currently this is all subject to
change but basically they can put little
two bits of information in there like
whether this object has ever had any
associated objects with it that need to
be cleaned up when it's de-allocated and
things like that
just real quick detail there so what do
these class structures look like since
every objective since every swift class
is also an objective-c class that means
that we can look at Objective C class
definitions to see what's going on and
Objective C class definitions are part
of the objective-c runtime which is open
source that's convenient so we can just
look in runtime th in the open source
dump there and if we look there and we
see what's going on this is what we get
so every class is also a valid object in
memory so if you remember an object
starts with an is a pointer so that
means every class starts with an is a
pointer as well so every class is also
an object a class has a class that's
called the meta class and you can follow
that rabbit hole all the way down until
you get very confused the class also
stores superclass so that allows you to
follow the chain up and essentially
explore the class hierarchy class stores
its name and stores a bunch of other
stuff it stores how big its objects are
it stores a list of instance variables
and methods and it's got a cache which
speeds up methods dispatch and then
Swift classes take all of that and they
add more stuff because Swift has more
stuff going on so if you look in the
Swift open source to see what's involved
there we've got some flags we got this
offsets a lot of bizarre stuff but
essentially a Swift class is the
objective-c class with more stuff on the
end and then this is an interesting part
after all of those fields it's a list of
methods again as an array of method
implementations so essentially it's the
C++ vtable approach again with some
extra stuff at the top that we can
ignore and so what that means is that
when you do a method call in Swift it
translates into essentially an array
lookup so you write object
and that translates into code like this
down here so essentially you take the
object you get that is a field out of it
and then you just index into it to get
the method pointer and then you jump to
it you essentially make a function call
based on that and so it's quick it's
efficient at runtime let's take a look
at what an object looks like when you
subclass a bunch of stuff so I made it
so a class a subclass a subclass of that
and so forth four levels deep and it
looks exactly the same so you got that
is a pointer at the top which tells you
what it is you've got the retain counts
below that and then the field of each of
all those classes just gets laid out
sequentially just like in C++ we saw
before so at runtime it's very simple
even though the class hierarchy we
looked at was kind of long and complex
let's take a look at arrays and Swift
arrays in Swift are value types which
means that they act like primitives
essentially when you assign x equals y
that conceptually creates a new array
which is coded lis separate from the
original this dump reveals that's
essentially a lie they are in fact
reference types under the hood the way
they're implemented so this array one
two three four five if you actually look
at it it's just a single pointer and
that points to one two three four and
then five after that which ran off the
end so it got truncated and so what's
going on with that is every aware array
that you work with is actually a pointer
to the storage and when you make a new
array you just get a new pointer to the
storage nothing really happens and it's
only when you actually modify it it will
go in and it will see oh someone else
references this I will create a copy and
then modify that copy
so it still references under the hood
you just don't see it until you run a
program like this then you see it let's
take a look at protocol types this is an
interesting aspect of Swift so here's a
Swift protocol it's got three methods in
it here is a struct which holds three
instances of that protocol right you can
use a protocol as a type itself and that
can hold an instance of anything that
implements that protocol
here is a struct which implements it
it's just got empty implementations of
those three methods it's also got a
field which is just an integer
containing the strange hex value that
hex value will spell out the word is
small in ASCII basically that's there so
that when I do the dump we can identify
it because it will search for that
string it will see show it in the
here's another struct this is a larger
one it's got four fields the first one
spells out large and the other ones just
contain one two and three repeated just
so that they show up nicely and finally
here is a class if this wants to advance
yeah my Wi-Fi is not cooperating there's
a class which same as the struct
essentially except it spells out class
instead and so we want to see how these
get represented so here we create an
instance of that protocol holder
containing one instance of the small
struct one instance of the large struct
in one instance of the class and if we
dump it out here's what we get the
larger view of this is very complicated
but we can see that struct in the list
of strings that it found it found small
so we can tell from this that small
struct actually gets stored in line that
protocol that field of protocol type is
able to store that struct in line but
the large struct does not get stored in
line and of course the class doesn't
because the class is a reference and
where did that large struct go because
structs normally get stored in line but
this one was large it ends up getting
stored off on the side here if you chase
the arrows around and essentially what
happened is this too big to fit the the
compiler can't know how big these things
are going to be so it places an
arbitrary size limit and when you go
over that limit then the compiler behind
your back boxes it up allocate something
dynamically and stores it over here so
here large
get stored off in the weeds somewhere if
you chase it down you actually look at
how these things are implemented a value
of protocol type holds five fields it
holds three arbitrary data fields and
then it holds some type metadata which
essentially tells you what it is and
then it holds a witness cable which is
like a V table for the protocol and
those three data fields are given over
to whatever the type needs them for so
if you got a struct which holds that
much stuff or less it gets stored in
line very efficiently and everything is
quick and as soon as you go over that
limit suddenly it has to get broken out
it gets boxed up it gets allocated
dynamically and you lose a lot of
efficiency and this is all hidden from
you you don't notice it until your code
gets slow so the witness table is
basically a V table is just an array of
implementations just like the C++ V
table and so that means that when you
make a method call on a protocol type it
looks a lot like a method call on an
object because you've got the special
table just for the protocol so when you
make a call you get a protocol type like
that you do PG make a call it translates
into something like this you just take
that you you look up the table by
looking up the forth word and the
protocol and then you use the offset in
the table that you know about because
method and then you just make the call
based on the function pointer and then
if you have a struct that's too big it
ends up looking like this instead of
having data fields that first data field
is actually a pointer to the real data
so everything gets stored off over here
you got the table over there and then
the methods here know that when they
need to do their stuff they have to go
up and chase that pointer and it's all
just handled behind your back and it's
not cooperating again indeed there we go
in um SAR a very interesting case in
Swift Swift has these high-level
enumeration types where you can have
associated data and all that or they can
just be very simple things here is a
simple case just five cases nothing
associated with them just ABCD and E
a struct which will hold those and the
result is those laid out very succinctly
zero one two three four each one gets a
different number they're one byte long
because we don't need more than one byte
to represent five values and it's all
very nice and compact here is a version
with a bra value so you can actually go
through and tell Swift I want my cases
to correspond to specific values so what
this does it says a is 1 B is 2 C is 3
these 4 e is 5 and let's see what that
looks like and an interesting thing it
does not change it doesn't go 1 2 3 4 5
it still goes 0 1 2 3 4 all right
running out of time here real quick we
just go through for a string if you can
do string raw values so a is whatever
and then BCD and you get defaults those
are just BCD uni and those still are 0 1
2 3 4 and essentially what's going on
here is the raw value can be stored off
in a separate table somewhere the
compiler knows about it there's no per
instance raw value of any kind so it can
just be 0 1 2 3 4 and somewhere else
there's a table that says 0 is whatever
1 is B 2 C and so forth alright let's
look at associated objects real quick
this is just an enum the first case has
an object associated with it and the
others do not and if we dump that out we
find that it has expanded because it
needs to be able to store that object
pointer but it's expanded intelligently
so the first thing isn't just a raw
object pointer and then the other ones
are just small integers and the compiler
is able to pack these so that it knows 0
2 4 & 6 can never be a valid pointer so
it's able to use that to distinguish
between those and then if we make it
larger we have an enum with ABCD and E
where they all have objects associated
with them and suddenly everything gets
bigger every entry is a pointer followed
by an integer so object pointer 0 object
pointer 1 object pointer to object
assigned each
case and The Associated value
essentially get laid out next to each
other the compiler is able to pack them
compactly for that one specific case but
not in the general sense
all right so wrapping up I'm just going
to kind of skip through these real quick
since we're behind on time we've got
real physical memory we've got
conceptual memory and then we've got
sort of the actual you know the
architecture of it all see just lays
things out nice and straightforward with
a little padding C++ objects
it'll be table at the top swift objects
get the same sort of thing but with more
stuff going on
protocol values end up taking up five
words of memory sometimes they can store
data in line but if you get too big they
don't and enums end up being packed in
many different ways depending on what's
going on there's our quick sum up which
I've just said and so you can learn a
lot by poking around it's a lot of fun
and sometimes it's useful and as they
asked me to remind you and as I did
before remember to rate the session in
the app and that's it so if there's
questions we can or if we have no time
for questions or okay can you use Swift
team angle with Pio and Xcode when
debugging I don't think there's any
built-in way to do that but what you can
do is just copy paste onto the command
swiftly mangle should be available in
the terminal if you wanted to I'm sure
you could build a little script you know
lld b is scriptable through python so
you could do that if that turned out be
useful yes okay so the question is any
versus any object in Swift three and
whether there are changes based on
think there would be changes in the
layout based on what you import because
there needs to be cross compatibility
between files that import Foundation the
files that don't so they would still
need to be the same but any in in Swift
three objective-c objects
as untyped objects now come in as any
instead of any object so there's
definitely a change there
I believe that's just essentially a
translation phase any is I think looks
like one of the protocol types where
it's a five word thing it's got three in
line and whatever and it's essentially
storing things that way and then there's
just a step where it takes that
Objective C pointer that comes in and
just kind of puts it in one of those
things the source code is online for my
thing it should run out more or less out
of the box so if you want to experiment
it and see what it does you know feel
free anything else nope all right oh yes
sure yeah so question is about the new
memory debugging facilities in xcode 8
and how it compares to my stuff so that
new memory debugging stuff is really
cool you can go through and it will just
essentially show you kind of graphs like
I showed here except they're live which
is really neat and I haven't played with
it a ton but I'm sure it's gonna be
really useful it's a little bit more
limited from what little I have done
with it in that it tries to well it's
got to work at runtime so it you know
has to be kind of limited in that
respect I believe it will not trace like
see pointers and things like that at
least not beyond a certain point it's
not going to be tracing global symbols
and things like that but as far as like
looking at playing Swift objects it's
really cool it'll show you the trees you
know how to show you this object points
to that object and I think it's gonna
skip over things like the pointers up to
the classes so it doesn't give you
everything but for what you care about
day to day it's it looks really cool
really useful all right looks like
that's it thank you very much for coming