Ответ 1
Метод f()
выполняется в интерпретированном фрейме. Интерпретированные кадры ведут себя иначе, чем JIT-скомпилированные фреймы. Вот как он выглядит в псевдокоде без цикла for:
1. Allocate dataSize bytes of memory
2. Store it into variable slot #1
3. Allocate dataSize bytes of memory
4. Store it into variable slot #1
Итак, у вас есть OutOfMemoryError
на шаге # 3, поскольку старый массив byte[]
все еще находится в переменной # 1. Однако добавление цикла for (фактически добавление переменной i
) делает другое:
1. Allocate dataSize bytes of memory
2. Store it into variable slot #1
3. Store 0 to slot #1 (thus byte[] array is now eligible for GC)
4. Do the for loop
5. Allocate dataSize bytes of memory
6. Store it into variable slot #2
Здесь, когда вы выделяете новый массив на шаге 5, первый массив уже может быть собран в мусор.
Обратите внимание, что компилятор JIT может вести себя умнее и отменить первый массив из переменной, поскольку он становится неиспользуемым (в вашем конкретном случае он вообще не выделяет его).
Также обратите внимание, что в вашем конкретном случае результат зависит от java-компилятора. ECJ (компилятор Eclipse) достаточно умен, чтобы не хранить первый массив в переменной вообще, поскольку он не используется. Таким образом, вы не получите OutOfMemoryError
в ECJ-компилированном классе даже без цикла for.
Для получения более подробной информации вы можете посмотреть вывод дизассемблирования байт-кода, предоставляемый утилитой javap
, и посмотреть, как повторно использовать слоты переменных.