С закрытием Scala, когда захваченные переменные начинают жить на куче JVM?

Связанный вопрос: Scala закрытия по сравнению с Java-классами → окончательный VS var

Интересно, когда Scala превращает переменные, захваченные в закрытие, в кучу, а не в стек. Я читаю книгу Scala Мартина Одерского, но пока я не нашел эту информацию. Может кто-нибудь объяснить, что за капотом?

Ответ 1

Анонимная функция (и фактически любая функция) в scala на самом деле является объектом (экземпляр Function*). Когда он создается, захват vals выполняется путем копирования vals во внутренние поля функционального объекта. В теле функции (то есть в методе object object apply) доступ к захваченным vals осуществляется путем доступа к этим полям.

Захват варов аналогичен, за исключением того, что компилятор должен добавить уровень косвенности: доступ к значению var осуществляется через некоторый скрытый изменяемый держатель (просто объект с изменяемым полем, указывающим на текущее значение var) и это этот держатель, который копируется в объект функции. При записи в var (либо локальным кодом, либо объектом функции) это поле владельца. Этот механизм гарантирует, что локальный код и функциональный код обрабатывают одни и те же данные, и оба видят друг друга.

Таким образом, ответ заключается в том, что захваченные vals и захваченный var оба всегда живут в куче (будь то непосредственно как поле объекта функции или как поле некоторого объекта-обертки)

Ответ 2

Я не знаю внутренности компилятора, но вот как это можно сделать. Для каждой локальной переменной компилятор поддерживает флаг, инициализированный как false. Всякий раз, когда используется переменная, компилятор проверяет, используется ли она внутри класса или закрытия, не содержащего объявление переменной; если это так, флаг установлен в true. В конце области переменной, если флаг все еще ложный, переменная может находиться в стеке. В противном случае он должен жить в куче.