How to measure the actual memory footprint of an instance of a struct?

I tried MemoryLayout.size(ofValue: theInstance) but it only returns the wrong size (might be the size of the pointer).

I would say, that this is quite a complex topic. The size of struct may be fairly simple to compute (using MemoryLayout). But when we talk about the footprint, what do you have in mind specifically? Do you consider type metadata, protocol conformances etc. to be part of the footprint?
This is much more of an issue with class instances, because a class instance may have a non-trivial structure. For example, a class instance may have a table of weak references. Or it may allocate space for its ancestors. It also may be a subclass of NSObject, which takes ObjC into consideration. Mike Ash did a talk on this topic. Exploring Swift Memory Layout • Mike Ash • GOTO 2016 - YouTube


Thanks for mentioning that. Let me narrow the range of this question to struct only.
For example: measuring how much memory gets occupied by a Dictionary struct which contains only structs.

Well, Dictionary is a complicated type. I would assume that it is some sort of hash table under the hood. If we put aside the optimizations of CoW types we discard the possibility, that the Dictionary is using an Obj-C "interop friendly" buffer, we can look into the open source version of apple/swift.

(There is in fact few pages worth of documentation in the source code swift/Dictionary.swift at main · apple/swift · GitHub .)

According to documentation, Swift - native dictionary looks like this:

// Native dictionary storage uses a data structure like this::
//   struct Dictionary<K,V>
//   +------------------------------------------------+
//   | enum Dictionary<K,V>._Variant                  |
//   | +--------------------------------------------+ |
//   | | [struct _NativeDictionary<K,V>             | |
//   | +---|----------------------------------------+ |
//   +----/-------------------------------------------+
//       /
//      |
//      V
//   class __RawDictionaryStorage
//   +-----------------------------------------------------------+
//   | <isa>                                                     |
//   | <refcount>                                                |
//   | _count                                                    |
//   | _capacity                                                 |
//   | _scale                                                    |
//   | _age                                                      |
//   | _seed                                                     |
//   | _rawKeys                                                  |
//   | _rawValue                                                 |
//   | [inline bitset of occupied entries]                       |
//   | [inline array of keys]                                    |
//   | [inline array of values]                                  |
//   +-----------------------------------------------------------+


// The native backing store is represented by three different classes:
// * `__RawDictionaryStorage`
// * `__EmptyDictionarySingleton` (extends Raw)
// * `_DictionaryStorage<K: Hashable, V>` (extends Raw)

This means, that even if we put all the other considerations aside, we need to know how to compute the footprint of a class instance first and then we would need to ... "parse" the contents of the class instance backing the Native Dictionary storage and assume the size of the buffer itself.


If it's just a POD struct:

struct S {
    struct D {
        var x: (Int, Int) = (0, 0)
    var d = D()
    var e = 3.14

Then MemoryLayout.size(ofValue: S()) or MemoryLayout<S>.size will give expected size, 24 in this case if I am not mistaken. For anything with references (String, Dictionary, Array, Data, etc) it is much more complex as @stuchlej said. For known types you can do an estimate, pseudocode:

func size(of any: Any) -> Int {
    if any is String -> string.utf8.count
    if any is Array -> reduce(0) { $0 + size(of: $1) }
    if any is Dictionary -> ditto plus the key size
    if any is Data -> data.count
    if any is UIImage -> hmm... perhaps width * height * 4 ???
    and so on

If you don't know what type would qualify as a POD @ShikiSuen , there is a hidden function that does just that:

1 Like

The question also gets less meaningful once parts of the “value” aren’t stored on the stack. If I say let newDict = existingDict, your footprint estimation function might say they’re each, say, 1024 bytes total, and so you might conclude that together they’d be 2048 bytes. But they aren’t, because Dictionary is documented to share storage for a simple copy like this until one value or the other is mutated; the actual total would be something like 1032 bytes. Similarly, you can imagine that there are other dictionaries nested inside these; if you modify one of the top-level dictionaries, new storage will be allocated, but the nested dictionaries will still be shared.

There’s definitely value in saying “how much memory is this value keeping alive”, but unfortunately it doesn’t have a simple answer in a language with ubiquitous reference counting (or garbage collection, for that matter). Tools like the memory graph debugger in Xcode start to matter if you don’t have your program’s general object ownership graph in your head already.