I hope you realize that all of this is deeply implementation-defined, both for Java and C++. That being said, Java's object model requires quite a bit of space.
C++ objects do not (generally) need any storage except what the members need. Note that (unlike Java, where everything user-defined is a reference type), client code can use an objects both as value type or as reference types, i.e. an object could store a pointer/reference to another object, or store the object directly without indirection. One additional pointer per object is necessary if there are any virtual
methods, but quite a few useful classes are designed to get along without polymorphism and don't need this. There is no GC metadata and no per-object lock. Thus class IntWrapper { int x; public: IntWrapper(int); ... };
objects need no more space than plain int
s, and can be placed directly (i.e. without indirection) in collections and other objects.
Arrays are tricky simply because there is no pre-made, common equivalent to a Java Array in C++. You could simply allocate a bunch of objects with new[]
(with absolutely no overhead/metadata) but there's no length field -- the implementation probably stores one but you can't access it. std::vector
is a dynamic array and thus has additional overhead and a larger interface. std::array
and C-style arrays (int arr[N];
), need a compile-time constant. In theory, it should be just the object's storage plus a single integer for the length -- but since you can get dynamic resizing and a fully-featured interface with very little extra space, you just go for that in practice. Note that all these, as well as all other collections, default to storing the objects by value, thus saving you indirection and the space for references, and improving cache behavior. You must explicitly store pointers (smart ones, please) to get indirection.
The above comparisons is not entirely fair, because some of these savings are afforded by not including features Java includes, and their C++ equivalent is often less optimized than the Java equivalent (*). The common way to implement virtual
in C++ imposes exactly as much overhead as the common way to implement virtual
in Java. To get a lock, you need a fully-features mutex object, which is most likely larger than a few bits. To get reference counting (not equivalent to GC, and shouldn't be used as such, but sometimes useful), you need a smart pointer which adds a reference count field. Unless the object is constructed carefully, reference count, smart pointer object, and referenced object are in completely separate locations, and even when you construct it right, the shared pointer may (must?) still be two pointers instead of one. Then again, good C++ style does not use these features enough for it to matter -- in practice, a well-written C++ library's objects use less. That does not necessarily mean less memory usage overall, but it does mean C++ has a good head start in this regard.
(*) For instance, you can get virtual calls, identity hash codes and locking with only one word for some objects (and two words for many other objects) by merging the type information with various flags, and removing lock bits for objects that are unlikely to need locks. See Space- and Time-Efficient Implementation of the Java Object Model (PDF) by David F. Bacon, Stephen J. Fink, and David Grove for a detailed explanation of this and other optimizations.
int
? If so, you should compare that toint
in Java, notInteger
- as long as your C++ ints are 32bits.