How Big Is That Class?
When you instantiate one of your classes, how much memory does it use? Today’s article tries out a lot of combinations and counts the bytes used. The conclusion is easy to remember and will give you a solid understanding of how much memory your app is using.
Let’s start with an empty class and use flash.sampler.getSize() on it:
class ClassZero{} // 32
We start off with 32 bytes of overhead. Just to confirm, let’s make sure that static variables don’t affect the size. Since they’re not part of a class instance, this shouldn’t be the case.
class ClassStaticVar{static var a:int;} // 32
Good. Now let’s proceed to add an integer:
class ClassInt1{var a:int;} // 40
Wait, the int
type only holds a 4 byte integer in AS3 so why did the memory usage jump up 8 bytes? Let’s explore by adding another integer:
class ClassInt2{var a:int;var b:int;} // 40
That int
took no more space than the class without it. This means that the version with just one int
had already reserved another 4 bytes of space which this int
used. Next let’s see if that pattern continues by adding another int
:
class ClassInt3{var a:int;var b:int;var c:int;} // 48
Yep, 8 more bytes were used by adding a single 4-byte int
. If the pattern holds true, the next int
will be “free”:
class ClassInt4{var a:int;var b:int;var c:int;var d:int;} // 48
And the next int
will take 8 more bytes:
class ClassInt5{var a:int;var b:int;var c:int;var d:int;var e:int;} // 56
We can keep adding int
fields with an easy-to-predict pattern:
class ClassInt6{var a:int;var b:int;var c:int;var d:int;var e:int;var f:int;} // 56 class ClassInt7{var a:int;var b:int;var c:int;var d:int;var e:int;var f:int;var g:int;} // 64
Next let’s try uint
, which is another 4-byte integer type:
class ClassUint1{var a:uint;} // 40 class ClassUint2{var a:uint;var b:uint;} // 40 class ClassUint3{var a:uint;var b:uint;var c:uint;} // 48 class ClassUint4{var a:uint;var b:uint;var c:uint;var d:uint;} // 48 class ClassUint5{var a:uint;var b:uint;var c:uint;var d:uint;var e:uint;} // 56 class ClassUint6{var a:uint;var b:uint;var c:uint;var d:uint;var e:uint;var f:uint;} // 56 class ClassUint7{var a:uint;var b:uint;var c:uint;var d:uint;var e:uint;var f:uint;var g:uint;} // 56
As expected, all the sizes for uint
are the same as with int
. So how about Boolean
? It just stores one bit of information, but as seen four years ago in The Size of Empty, it’s actually using 4 whole bytes! If still true, the class sizes should look identical to the int
and uint
versions.
class ClassBool1{var a:Boolean;} // 40 class ClassBool2{var a:Boolean;var b:Boolean;} // 40 class ClassBool3{var a:Boolean;var b:Boolean;var c:Boolean;} // 48 class ClassBool4{var a:Boolean;var b:Boolean;var c:Boolean;var d:Boolean;} // 48 class ClassBool5{var a:Boolean;var b:Boolean;var c:Boolean;var d:Boolean;var e:Boolean;} // 56 class ClassBool6{var a:Boolean;var b:Boolean;var c:Boolean;var d:Boolean;var e:Boolean;var f:Boolean;} // 56 class ClassBool7{var a:Boolean;var b:Boolean;var c:Boolean;var d:Boolean;var e:Boolean;var f:Boolean;var g:Boolean;} // 64
Sadly, this is still the case with Boolean
. A single bit of information is being stored using 32 bits.
Now for something a bit different: Number
. We know that it’s a 64-bit (8-byte) floating point value, so it should take up more space and yield larger memory sizes.
class ClassNumber1{var a:Number;} // 40 class ClassNumber2{var a:Number;var b:Number;} // 48 class ClassNumber3{var a:Number;var b:Number;var c:Number;} // 56 class ClassNumber4{var a:Number;var b:Number;var c:Number;var d:Number;} // 64 class ClassNumber5{var a:Number;var b:Number;var c:Number;var d:Number;var e:Number;} // 72 class ClassNumber6{var a:Number;var b:Number;var c:Number;var d:Number;var e:Number;var f:Number;} // 80 class ClassNumber7{var a:Number;var b:Number;var c:Number;var d:Number;var e:Number;var f:Number;var g:Number;} // 88
Indeed, Number
is taking up its full 8 bytes with each addition. There is no extra 4 bytes of padding like with int
, uint
, and Boolean
.
Next we’ll leave the land of basic types and explore Object
.
class ClassObject1{var a:Object;} // 40 class ClassObject2{var a:Object;var b:Object;} // 48 class ClassObject3{var a:Object;var b:Object;var c:Object;} // 56 class ClassObject4{var a:Object;var b:Object;var c:Object;var d:Object;} // 64 class ClassObject5{var a:Object;var b:Object;var c:Object;var d:Object;var e:Object;} // 72 class ClassObject6{var a:Object;var b:Object;var c:Object;var d:Object;var e:Object;var f:Object;} // 80 class ClassObject7{var a:Object;var b:Object;var c:Object;var d:Object;var e:Object;var f:Object;var g:Object;} // 88
The Object
sizes match those of Number
, indicating that it too takes up 8 bytes. This makes sense given that I’m testing with a 64-bit (x64) CPU where pointers/references take up, well, 64 bits or 8 bytes.
Untyped variables are also pointers/references, so they should be the same
class ClassUntyped1{var a:*;} // 40 class ClassUntyped2{var a:*;var b:*;} // 48 class ClassUntyped3{var a:*;var b:*;var c:*;} // 56 class ClassUntyped4{var a:*;var b:*;var c:*;var d:*;} // 64 class ClassUntyped5{var a:*;var b:*;var c:*;var d:*;var e:*;} // 72 class ClassUntyped6{var a:*;var b:*;var c:*;var d:*;var e:*;var f:*;} // 80 class ClassUntyped7{var a:*;var b:*;var c:*;var d:*;var e:*;var f:*;var g:*;} // 88
And so should String
variables, since they too are just pointers/references:
class ClassString1{var a:String;} // 40 class ClassString2{var a:String;var b:String;} // 48 class ClassString3{var a:String;var b:String;var c:String;} // 56 class ClassString4{var a:String;var b:String;var c:String;var d:String;} // 64 class ClassString5{var a:String;var b:String;var c:String;var d:String;var e:String;} // 72 class ClassString6{var a:String;var b:String;var c:String;var d:String;var e:String;var f:String;} // 80 class ClassString7{var a:String;var b:String;var c:String;var d:String;var e:String;var f:String;var g:String;} // 88
Really, this should hold true for any pointer/reference type that isn’t copied when you assign it to a variable or pass it to a function. Let’s just make sure by using our empty class:
class ClassClassZero1{var a:ClassZero;} // 40 class ClassClassZero2{var a:ClassZero;var b:ClassZero;} // 48 class ClassClassZero3{var a:ClassZero;var b:ClassZero;var c:ClassZero;} // 56 class ClassClassZero4{var a:ClassZero;var b:ClassZero;var c:ClassZero;var d:ClassZero;} // 64 class ClassClassZero5{var a:ClassZero;var b:ClassZero;var c:ClassZero;var d:ClassZero;var e:ClassZero;} // 72 class ClassClassZero6{var a:ClassZero;var b:ClassZero;var c:ClassZero;var d:ClassZero;var e:ClassZero;var f:ClassZero;} // 80 class ClassClassZero7{var a:ClassZero;var b:ClassZero;var c:ClassZero;var d:ClassZero;var e:ClassZero;var f:ClassZero;var g:ClassZero;} // 88
Now let’s delve a little deeper into these two sizes—4 bytes and 8 bytes—to see what happens when you mix and match them. First, let’s use one 4 byte value (int
) followed by one 8 byte value (Number
).
class Class48{var a:int;var b:Number;} // 48
Remember that 32 bytes were used for the empty class. When we added an int
, the size grew by its 4 bytes plus 4 bytes of padding. When we add this 8-byte Number
we shouldn’t have enough room in the padding and should spill over into the next 4 bytes for a total of 44 bytes. However, the 4 byte values all grew in chunks of 8. This seems to be what’s happening here since we ended up with a 48 byte size.
We should have 4 more bytes of padding available, so let’s try to use it by adding another 4-byte int
.
class Class484{var a:int;var b:Number;var c:int;} // 48
Since the size didn’t grow, this seems to validate the theory. There should be no padding left now. Adding another Number
should expand the size by 8 bytes. Adding another int
should expand the size by 4 bytes with another 4 bytes of padding.
class Class4848{var a:int;var b:Number;var c:int;var d:Number;} // 56 class Class4844{var a:int;var b:Number;var c:int;var d:int;} // 56
The next int
we add will fit in the padding, but the next Number
won’t fit and we’ll need to expand to the next 8 byte level.
class Class48444{var a:int;var b:Number;var c:int;var d:int;var e:int;} // 56 class Class48484{var a:int;var b:Number;var c:int;var d:Number;var e:int;} // 64
That about sums up all of the 4 and 8 byte values. One final piece of information to confirm is that functions don’t take up any space. Let’s start with just one function:
class ClassFunction{public function a():void{}} // 32
This is the empty size of 32 bytes, so the function didn’t add to the size. Are derived classes the same?
class ClassDerived extends ClassFunction{} // 32
Yes, derived classes don’t add to the size either. How about overriding functions?
class ClassDerivedOverride extends ClassFunction{override public function a():void{}} // 32
Overriding also doesn’t grow the size. One final check: static functions.
class ClassStaticFunction{public static function a():void{}} // 32
No problem there, either.
In conclusion, class sizes are always a multiple of 8. An int
, uint
, or Boolean
will result in 4 extra bytes of padding if not followed by another int
, uint
, or Boolean
. Static variables, extending classes, and functions of all types don’t add anything to the size.
Here’s the full test source code if you want to try this out yourself:
package { import flash.display.*; import flash.utils.*; import flash.text.*; import flash.sampler.*; import flash.system.*; public class SizeOfClasses extends Sprite { private var logger:TextField = new TextField(); private function row(...cols): void { logger.appendText(cols.join(",") + "\n"); } public function SizeOfClasses() { stage.align = StageAlign.TOP_LEFT; stage.scaleMode = StageScaleMode.NO_SCALE; logger.autoSize = TextFieldAutoSize.LEFT; addChild(logger); if (!Capabilities.isDebugger) { row("This app requires a debug player"); return; } row("ClassZero", getSize(new ClassZero())); row("ClassStaticVar", getSize(new ClassStaticVar())); row("ClassInt1", getSize(new ClassInt1())); row("ClassInt2", getSize(new ClassInt2())); row("ClassInt3", getSize(new ClassInt3())); row("ClassInt4", getSize(new ClassInt4())); row("ClassInt5", getSize(new ClassInt5())); row("ClassInt6", getSize(new ClassInt6())); row("ClassInt7", getSize(new ClassInt7())); row("ClassUint1", getSize(new ClassUint1())); row("ClassUint2", getSize(new ClassUint2())); row("ClassUint3", getSize(new ClassUint3())); row("ClassUint4", getSize(new ClassUint4())); row("ClassUint5", getSize(new ClassUint5())); row("ClassUint6", getSize(new ClassUint6())); row("ClassUint7", getSize(new ClassUint7())); row("ClassBool1", getSize(new ClassBool1())); row("ClassBool2", getSize(new ClassBool2())); row("ClassBool3", getSize(new ClassBool3())); row("ClassBool4", getSize(new ClassBool4())); row("ClassBool5", getSize(new ClassBool5())); row("ClassBool6", getSize(new ClassBool6())); row("ClassBool7", getSize(new ClassBool7())); row("ClassNumber1", getSize(new ClassNumber1())); row("ClassNumber2", getSize(new ClassNumber2())); row("ClassNumber3", getSize(new ClassNumber3())); row("ClassNumber4", getSize(new ClassNumber4())); row("ClassNumber5", getSize(new ClassNumber5())); row("ClassNumber6", getSize(new ClassNumber6())); row("ClassNumber7", getSize(new ClassNumber7())); row("ClassObject1", getSize(new ClassObject1())); row("ClassObject2", getSize(new ClassObject2())); row("ClassObject3", getSize(new ClassObject3())); row("ClassObject4", getSize(new ClassObject4())); row("ClassObject5", getSize(new ClassObject5())); row("ClassObject6", getSize(new ClassObject6())); row("ClassObject7", getSize(new ClassObject7())); row("ClassUntyped1", getSize(new ClassUntyped1())); row("ClassUntyped2", getSize(new ClassUntyped2())); row("ClassUntyped3", getSize(new ClassUntyped3())); row("ClassUntyped4", getSize(new ClassUntyped4())); row("ClassUntyped5", getSize(new ClassUntyped5())); row("ClassUntyped6", getSize(new ClassUntyped6())); row("ClassUntyped7", getSize(new ClassUntyped7())); row("ClassString1", getSize(new ClassString1())); row("ClassString2", getSize(new ClassString2())); row("ClassString3", getSize(new ClassString3())); row("ClassString4", getSize(new ClassString4())); row("ClassString5", getSize(new ClassString5())); row("ClassString6", getSize(new ClassString6())); row("ClassString7", getSize(new ClassString7())); row("ClassClassZero1", getSize(new ClassClassZero1())); row("ClassClassZero2", getSize(new ClassClassZero2())); row("ClassClassZero3", getSize(new ClassClassZero3())); row("ClassClassZero4", getSize(new ClassClassZero4())); row("ClassClassZero5", getSize(new ClassClassZero5())); row("ClassClassZero6", getSize(new ClassClassZero6())); row("ClassClassZero7", getSize(new ClassClassZero7())); row("Class48", getSize(new Class48())); row("Class484", getSize(new Class484())); row("Class4844", getSize(new Class4844())); row("Class4848", getSize(new Class4848())); row("Class48444", getSize(new Class48444())); row("Class48484", getSize(new Class48484())); row("ClassFunction", getSize(new ClassFunction())); row("ClassDerived", getSize(new ClassDerived())); row("ClassDerivedOverride", getSize(new ClassDerivedOverride())); row("ClassStaticFunction", getSize(new ClassStaticFunction())); } } } class ClassZero{} class ClassStaticVar{static var a:int;} class ClassInt1{var a:int;} class ClassInt2{var a:int;var b:int;} class ClassInt3{var a:int;var b:int;var c:int;} class ClassInt4{var a:int;var b:int;var c:int;var d:int;} class ClassInt5{var a:int;var b:int;var c:int;var d:int;var e:int;} class ClassInt6{var a:int;var b:int;var c:int;var d:int;var e:int;var f:int;} class ClassInt7{var a:int;var b:int;var c:int;var d:int;var e:int;var f:int;var g:int;} class ClassUint1{var a:uint;} class ClassUint2{var a:uint;var b:uint;} class ClassUint3{var a:uint;var b:uint;var c:uint;} class ClassUint4{var a:uint;var b:uint;var c:uint;var d:uint;} class ClassUint5{var a:uint;var b:uint;var c:uint;var d:uint;var e:uint;} class ClassUint6{var a:uint;var b:uint;var c:uint;var d:uint;var e:uint;var f:uint;} class ClassUint7{var a:uint;var b:uint;var c:uint;var d:uint;var e:uint;var f:uint;var g:uint;} class ClassBool1{var a:Boolean;} class ClassBool2{var a:Boolean;var b:Boolean;} class ClassBool3{var a:Boolean;var b:Boolean;var c:Boolean;} class ClassBool4{var a:Boolean;var b:Boolean;var c:Boolean;var d:Boolean;} class ClassBool5{var a:Boolean;var b:Boolean;var c:Boolean;var d:Boolean;var e:Boolean;} class ClassBool6{var a:Boolean;var b:Boolean;var c:Boolean;var d:Boolean;var e:Boolean;var f:Boolean;} class ClassBool7{var a:Boolean;var b:Boolean;var c:Boolean;var d:Boolean;var e:Boolean;var f:Boolean;var g:Boolean;} class ClassNumber1{var a:Number;} class ClassNumber2{var a:Number;var b:Number;} class ClassNumber3{var a:Number;var b:Number;var c:Number;} class ClassNumber4{var a:Number;var b:Number;var c:Number;var d:Number;} class ClassNumber5{var a:Number;var b:Number;var c:Number;var d:Number;var e:Number;} class ClassNumber6{var a:Number;var b:Number;var c:Number;var d:Number;var e:Number;var f:Number;} class ClassNumber7{var a:Number;var b:Number;var c:Number;var d:Number;var e:Number;var f:Number;var g:Number;} class ClassObject1{var a:Object;} class ClassObject2{var a:Object;var b:Object;} class ClassObject3{var a:Object;var b:Object;var c:Object;} class ClassObject4{var a:Object;var b:Object;var c:Object;var d:Object;} class ClassObject5{var a:Object;var b:Object;var c:Object;var d:Object;var e:Object;} class ClassObject6{var a:Object;var b:Object;var c:Object;var d:Object;var e:Object;var f:Object;} class ClassObject7{var a:Object;var b:Object;var c:Object;var d:Object;var e:Object;var f:Object;var g:Object;} class ClassUntyped1{var a:*;} class ClassUntyped2{var a:*;var b:*;} class ClassUntyped3{var a:*;var b:*;var c:*;} class ClassUntyped4{var a:*;var b:*;var c:*;var d:*;} class ClassUntyped5{var a:*;var b:*;var c:*;var d:*;var e:*;} class ClassUntyped6{var a:*;var b:*;var c:*;var d:*;var e:*;var f:*;} class ClassUntyped7{var a:*;var b:*;var c:*;var d:*;var e:*;var f:*;var g:*;} class ClassString1{var a:String;} class ClassString2{var a:String;var b:String;} class ClassString3{var a:String;var b:String;var c:String;} class ClassString4{var a:String;var b:String;var c:String;var d:String;} class ClassString5{var a:String;var b:String;var c:String;var d:String;var e:String;} class ClassString6{var a:String;var b:String;var c:String;var d:String;var e:String;var f:String;} class ClassString7{var a:String;var b:String;var c:String;var d:String;var e:String;var f:String;var g:String;} class ClassClassZero1{var a:ClassZero;} class ClassClassZero2{var a:ClassZero;var b:ClassZero;} class ClassClassZero3{var a:ClassZero;var b:ClassZero;var c:ClassZero;} class ClassClassZero4{var a:ClassZero;var b:ClassZero;var c:ClassZero;var d:ClassZero;} class ClassClassZero5{var a:ClassZero;var b:ClassZero;var c:ClassZero;var d:ClassZero;var e:ClassZero;} class ClassClassZero6{var a:ClassZero;var b:ClassZero;var c:ClassZero;var d:ClassZero;var e:ClassZero;var f:ClassZero;} class ClassClassZero7{var a:ClassZero;var b:ClassZero;var c:ClassZero;var d:ClassZero;var e:ClassZero;var f:ClassZero;var g:ClassZero;} class Class48{var a:int;var b:Number;} class Class484{var a:int;var b:Number;var c:int;} class Class4848{var a:int;var b:Number;var c:int;var d:Number;} class Class4844{var a:int;var b:Number;var c:int;var d:int;} class Class48444{var a:int;var b:Number;var c:int;var d:int;var e:int;} class Class48484{var a:int;var b:Number;var c:int;var d:Number;var e:int;} class ClassFunction{public function a():void{}} class ClassDerived extends ClassFunction{} class ClassDerivedOverride extends ClassFunction{override public function a():void{}} class ClassStaticFunction{public static function a():void{}}
Spot a bug? Have a question or suggestion? Post a comment!
#1 by Vikas on May 19th, 2014 ·
I wonder if 4-byte padding for int/uint cases won’t happen on 32bit CPU
#2 by jackson on May 19th, 2014 ·
Good point. I ran it on a 32-bit Windows standalone/projector Flash Player and got a base size of 16 bytes (half) and no padding of 4 byte values to 8 bytes. So
ClassInt7
takes only 44 bytes (not 64) andClassNumber7
takes 72 bytes (not 88).#3 by Soong on May 20th, 2014 ·
Results@{FP:14.0,Browser:IE11,OS:WIN64SP1}
ClassZero,16
ClassStaticVar,16
ClassInt1,20
ClassInt2,24
ClassInt3,28
ClassInt4,32
ClassInt5,36
ClassInt6,40
ClassInt7,44
ClassUint1,20
ClassUint2,24
ClassUint3,28
ClassUint4,32
ClassUint5,36
ClassUint6,40
ClassUint7,44
ClassBool1,20
ClassBool2,24
ClassBool3,28
ClassBool4,32
ClassBool5,36
ClassBool6,40
ClassBool7,44
ClassNumber1,24
ClassNumber2,32
ClassNumber3,40
ClassNumber4,48
ClassNumber5,56
ClassNumber6,64
ClassNumber7,72
ClassObject1,20
ClassObject2,24
ClassObject3,28
ClassObject4,32
ClassObject5,36
ClassObject6,40
ClassObject7,44
ClassUntyped1,20
ClassUntyped2,24
ClassUntyped3,28
ClassUntyped4,32
ClassUntyped5,36
ClassUntyped6,40
ClassUntyped7,44
ClassString1,20
ClassString2,24
ClassString3,28
ClassString4,32
ClassString5,36
ClassString6,40
ClassString7,44
ClassClassZero1,20
ClassClassZero2,24
ClassClassZero3,28
ClassClassZero4,32
ClassClassZero5,36
ClassClassZero6,40
ClassClassZero7,44
Class48,32
Class484,32
Class4844,40
Class4848,40
Class48444,40
Class48484,48
ClassFunction,16
ClassDerived,16
ClassDerivedOverride,16
ClassStaticFunction,16
#4 by joe on May 21st, 2014 ·
very useful for memory optimisation
#5 by é€é¹¿ on May 22nd, 2014 ·
ä½ è¯´çš„æ˜¯å¯¹çš„ï¼Œæˆ‘å·²ç»éªŒè¯è¿‡äº†