3

I try to create an array of Integers (i tried with own object but the same happened with int) , with size of 30 million. i keep getting "OutOfMemoryError: Java heap space"

Integer [] index = new Integer[30000000];
for (int i = 0 ; i < 30000000 ; i++){
    index[i] = i;
}

i checked the total heap space, using "Runtime.getRuntime().totalMemory()" and "maxMemory()" and saw that i start with 64 MB and the max is 900+ MB, and during the run i get to 900+ on the heap and crush.

now i know that Integer takes 4 bytes, so even if i multiply 30*4*1000000 i should still only get about 150-100 mega.

if i try with a primitive type, like int, it works.

how could i fix it ?

trincot
  • 317,000
  • 35
  • 244
  • 286
Ben2307
  • 1,003
  • 3
  • 17
  • 31

5 Answers5

8

Java's int primitive will take up 4 bytes but if you use a ValueObject like Integer it's going to take up much more space. Depending on your machine a reference alone could take up 32 or 64 bits + the size of the primitive it is wrapping.

You should probably just use primitive ints if space is an issue. Here is a very good SO answer that explains this topic in more detail.

Community
  • 1
  • 1
Andrew White
  • 52,720
  • 19
  • 113
  • 137
  • I believe that his use of Integer was for demonstrative purposes only. I believe that the real code uses an array of non-Integer custom class objects. – Hovercraft Full Of Eels May 21 '11 at 13:22
  • you are correct, i use my own object. and i can't understand how it can take so much space – Ben2307 May 22 '11 at 08:36
  • Yeah, it's the reference size overhead that's getting you. If you are using Java 64bit you can also try -XX:+UseCompressedOops to help reduce the size of references. – Andrew White May 22 '11 at 14:50
  • CompressedOoops will roughly halve the size of the `Integer[]` array, but the `Integer` objects remain the same size. – Stephen C Dec 05 '21 at 22:48
4

Lets assume that we are talking about a 32bit OpenJDK-based JVM.

  • Each Integer object has 1 int field - occupying 4 bytes.
  • Each Integer object has 2 header words - occupying 8 bytes.
  • The granularity of allocation is (I believe) 2 words - 4 bytes of padding.
  • The Integer[] has 1 reference for each array element / position - 4 bytes.

So the total is 20 bytes per array element. 20 x 30 x 1,000,000 = 600,000,000 Mbytes. Now add the fact that the generational collector will allocate at least 3 object spaces of various sizes, and that could easily add up to 900+ Mbytes.


how could i fix it ?

  • Use int[] instead of Integer.
  • If the Integer values mostly represent numbers in the range -128 to + 127, allocate them with Integer.valueOf(int). The JLS guarantees that Integer objects created that way will be shared. (Note that when an Integer is created by auto-boxing, then JLS stipulates that valueOf is used. So, in fact, this "fix" has already been applied in your example.)
  • If your Integer values mostly come from a larger but still small domain, consider implementing your own cache for sharing Integer objects.

My question was about Integer as an example, in my program i use my own object that only holds an array of bytes (max size of 4). when i create it, it takes a lot more then 4 bytes on the memory.

Yes, it will do.

Let's assume your class is defined like this:

public class MyInt {
    private byte[] bytes = new byte[4];
}

Each MyInt will occupy:

  • MyInt header words - 8 bytes
  • MyInt.bytes field - 4 byte
  • Padding - 4 bytes
  • Header words for the byte array - 12 bytes
  • Array content - 4 bytes

Now add the space taken by the MyInt reference:

  • Reference to each MyInt - 4 bytes

Grand total - 36 bytes per MyInt element of a MyInt[].

Compare that with 20 bytes per Integer element of an Integer[] or 4 bytes per int element of an int[].


How to fix that case?

Well an array of 4 bytes contains 32 bits of data. That can be encoded as int. So the fix is the same as before. Use an int[] instead of a MyInt[], or (possibly) adapt one of the other ideas discussed above.

Alternatively, make the heap larger, or use a database or something like that so that the data doesn't need to be held in RAM.

Stephen C
  • 698,415
  • 94
  • 811
  • 1,216
  • My question was about Integer as an example, in my program i use my own object that only holds an array of bytes (max size of 4). when i create it, it takes a lot more then 4 bytes on the memory. – Ben2307 May 22 '11 at 08:24
0

Integer is an object which will take more than 4 bytes. How much more is implementation dependent. Do you really need Integer? The only benefit is that it can be null. Perhaps you could use a "sentinal value" instead; say, -1, or Integer.MIN_VALUE.

dty
  • 18,795
  • 6
  • 56
  • 82
0

Perhaps you should be using a database rather than a huge array, but if you must use a huge array of objects, have you tried increasing the Java memory size by using a the -Xms command line argument when running the Java application launcher?

Hovercraft Full Of Eels
  • 283,665
  • 25
  • 256
  • 373
0

This is not what you are looking for but the optimal solution is to use a function instead of an array in this simple example.

static int index(int num) {
    return num;
}

If you have a more realistic example, there may be other optimisations you can use.

Peter Lawrey
  • 525,659
  • 79
  • 751
  • 1,130