Class vs tuple - memory consumption

417 views
Skip to first unread message

Michał Makowski

unread,
Jun 15, 2016, 9:43:18 AM6/15/16
to scala-user
Hi,

Can someone help me to solve this puzzle? In my application I have replaced tuples with classes to make it more readable but now it is taking more memory. 
I have developed two test programs:

class_test.scala:
class Container(val long1: Long, val long2: Long, val int1: Int) {
}

object Test1 {
def main(args: Array[String]) {
val objects = Array.fill[Container](args(0).toInt)(new Container(0,0,0));
val runtime = Runtime.getRuntime
val mb = 1024*1024;
println(" " + (runtime.totalMemory - runtime.freeMemory) / mb + " MB")
}

}

tuple_test.scala:
object Test2 {
def main(args: Array[String]) {
val objects = Array.fill[(Long,Long,Int)](args(0).toInt)((0,0,0));

val runtime = Runtime.getRuntime
val mb = 1024*1024;
println(" " + (runtime.totalMemory - runtime.freeMemory) / mb + " MB")

}

}

I have tested both programs for several array lengths and got following results:

|----------------------------------------------------|
|              Memory usage tests                    |
| Array length | class_test.scala | tuple_test.scala |
|----------------------------------------------------|
| 1000         | 2 MB             | 2 MB             |
| 10000        | 2 MB             | 2 MB             |
| 100000       | 5 MB             | 5 MB             |
| 1000000      | 35 MB            | 27 MB            |
| 2000000      | 69 MB            | 54 MB            |
| 3000000      | 103 MB           | 80 MB            |
| 4000000      | 138 MB           | 107 MB           |
| 5000000      | 172 MB           | 134 MB           |
| 6000000      | OOM error        | 161 MB           |
| 7000000      | OOM error        | 187 MB           |
|----------------------------------------------------|

Can you tell me why it gives such a difference (ca. 30%) ? According to books tuples are implemented as instance of TupleX[Y] case class so still a class.

Dennis Haupt

unread,
Jun 15, 2016, 10:08:14 AM6/15/16
to "Michał Makowski", scala-user
use a profiler. runtime.getxxx is not realiable for object size measurements because of the gc
Gesendet: Mittwoch, 15. Juni 2016 um 15:35 Uhr
Von: "Michał Makowski" <michal....@gmail.com>
An: scala-user <scala...@googlegroups.com>
Betreff: [scala-user] Class vs tuple - memory consumption
--
You received this message because you are subscribed to the Google Groups "scala-user" group.
To unsubscribe from this group and stop receiving emails from it, send an email to scala-user+...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

Brian Maso

unread,
Jun 15, 2016, 10:50:35 AM6/15/16
to Michał Makowski, scala-user
My hunch would be that the Tuple3 your are using is using Object references, not specialized for primitive type references, so there's a lot more overhead. A little runtime exploration with a debugger would be able to tell you if that is the case.

Brian Maso

--
You received this message because you are subscribed to the Google Groups "scala-user" group.
To unsubscribe from this group and stop receiving emails from it, send an email to scala-user+...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.



--
Best regards,
Brian Maso
(949) 395-8551
Follow me: @bmaso
br...@blumenfeld-maso.com

Dennis Haupt

unread,
Jun 15, 2016, 11:00:29 AM6/15/16
to Brian Maso, "Michał Makowski", scala-user

the numbers indicate the opposite
 
Gesendet: Mittwoch, 15. Juni 2016 um 16:50 Uhr
Von: "Brian Maso" <br...@blumenfeld-maso.com>
An: "Michał Makowski" <michal....@gmail.com>
Cc: scala-user <scala...@googlegroups.com>
Betreff: Re: [scala-user] Class vs tuple - memory consumption

Brian Maso

unread,
Jun 15, 2016, 12:05:36 PM6/15/16
to Dennis Haupt, Michał Makowski, scala-user
Oops -- I read the column headers backwards! I will refrain from guessing any more...

Brian Maso

Rüdiger Klaehn

unread,
Jun 15, 2016, 12:35:30 PM6/15/16
to Michał Makowski, scala-user
I don't have time to measure this, but I would guess that the issue is as follows.

Tuple3 is not specialized, so it's just three object references. An object reference is 32bit even on 64bit CPUs, unless you have UseCompressedOops disabled (it is enabled by default). So a Tuple3 with three object references (12 bytes + object overhead) is smaller than your class Container (20 bytes + object overhead).

Now, if you would use real data (e.g. random numbers), the boxed longs *referenced* from the tuples would use a lot more memory. But since you are using 0 all the time, you are always pointing to the same boxed long instance. Small integers are cached when boxing.

So the conclusion is that the Container is much more compact for real data, but your test is broken.

Cheers,

Rüdiger

Rex Kerr

unread,
Jun 15, 2016, 12:45:46 PM6/15/16
to Rüdiger Klaehn, Michał Makowski, scala-user
That sounds right to me.  I was thinking something similar.

Note that if the compiler were smarter, the second test would be even more broken because it would realize (0, 0, 0) is a constant that can be lifted out of the array creation loop, giving you only an increase of the size of the array not any additional memory usage.

  --Rex

Michał Makowski

unread,
Jun 15, 2016, 1:01:06 PM6/15/16
to Rex Kerr, Rüdiger Klaehn, scala-user
Makes sense. Using random numbers has changed the values no change for class but with tuple it uses way more memory.
Now it's clear.  I have no java/jvm experience. I'm C++ developer and this is quite different world :)  needs some investigation for good understanding. 

Thanks

Reply all
Reply to author
Forward
0 new messages