Existem duas estruturas de dados onde o Java pode armazenar dados: o stack e o heap.
O stack é usado para três coisas:
- Variáveis locais;
- Passagem de parâmetros;
- Endereço de retorno de métodos.
Para as variáveis locais e os parâmetros, são armazenadas diretamente no stack: os tipos primitivos (por exemplo, em uma máquina Intel ou AMD x86 eles ocupam 4 ou 8 bytes no stack, se não me engano; e em uma máquina de 64 bits sempre ocupam 8 bytes no stack), e as referências para os objetos (que ocupam, cada uma, 4 bytes no stack para máquinas de 32 bits e 8 para máquinas de 64 bits).
O heap serve para armazenar os objetos.
No caso de um array, o que é armazenado no stack é a referência para o array; o array em si é armazenado no heap, mesmo que seja um array de tipos primitivos.
Pode ser que a JVM escolha fazer a seguinte otimização: se o objeto criado for muito pequeno (por exemplo, um java.awt.Point, que contém 2 ints) e tiver tempo de vida muito curto (ou seja, referido por uma variável local de escopo pequeno), esse objeto seja criado diretamente no stack. Mas isso é uma otimização que é apenas “teoricamente possível”; na prática não é feita pela JVM da Sun, se não me engano.