Get the size (in bytes) of an object on the heap

2019-01-22 22:53发布

问题:

I'm aware you can use MemoryLayout<T>.size to get the size of a type T.

For example: MemoryLayout<Int32>.size // 4

However, for class instances (objects), MemoryLayout<T>.size returns the size of the reference to the object (8 bytes on 64 bit machines), not the size of the actual objects on the heap.

class ClassA { // Objects should be at least 8 bytes
    let x: Int64 = 0
}

class ClassB  {// Objects should be at least 16 bytes
    let x: Int64 = 0
    let y: Int64 = 0
}

MemoryLayout<ClassA>.size // 8
MemoryLayout<ClassB>.size // 8, as well :(

How can I get the size of the objects themselves?

For those wondering, I have no real need for this, I'm just exploring around Swift and its interoperability with C.

回答1:

One option on Apple platforms, because Swift classes are currently built on top of Objective-C classes there, would be to use the Obj-C runtime function class_getInstanceSize, which gives you the size in bytes of an instance of the class, including any padding.

// on a 64-bit machine (1 word == 8 bytes)...

import Foundation

class C {}
print(class_getInstanceSize(C.self)) // 16 bytes metadata for empty class 
                                     // (isa ptr + ref count)

class C1 {
    var i = 0
    var i1 = 0
    var b = false
}

print(class_getInstanceSize(C1.self)) // 40 bytes
// (16 metadata + 24 ivars, 8 for i + 8 for i1 + 1 for b + 7 padding)


回答2:

As far as I tested in the Playground, this function returns seemingly significant value:

func heapSize(_ obj: AnyObject) -> Int {
    return malloc_size(Unmanaged.passRetained(obj).toOpaque())
}

class MyClass {
    //no properites...
}
let myObj = MyClass()
print(heapSize(myObj)) //->16

class MyBiggerClass {
    var str: String?
    var i: Int = 0
}
let myBiggerObj = MyBiggerClass()
print(heapSize(myBiggerObj)) //->64

Seems the current Swift runtime uses malloc-compatible something to allocate memory in heap. (malloc gives some padding to fit the allocated size to power to 2, when allocating small chunks. So, the actually needed size for the instance may be smaller than the malloc_size.)

I haven't tested how far this would work, and undocumented behaviours just depending on the current implementation would change at any time in the future without any notifications.

But if you actually know that, this can be a good starting point for exploration.