Очистить память, используемую PHP

У меня возникает интересная проблема. Я использую PHPUnit, и мои тесты занимают больше памяти каждый раз, когда я их запускаю. Т.е....

2,25 МБ

2,5 МБ

3,0 МБ

3,5 МБ .......

Кто-нибудь знает, как очистить память, которая потребляется, и может ли кто-нибудь посоветовать мне изучить это подробно? Непосредственная проблема заключается в том, что некоторые из моих более крупных тестов заканчиваются из памяти, и просто продолжая увеличивать максимальный объем выделения памяти в PHP, недостаточно. Мне нужно знать, почему тест PHPUnit, запущенный из командной строки, будет иметь память использование, которое "прилипает" между прогонами.

Ответ 1

Calvin, как обсуждалось в chat, из-за отсутствия функций reset.

При тестировании мы должны убедиться, что тестовая среда согласована, чтобы мы могли получать точные результаты. Computing - Input/Output, поэтому мы должны использовать Fixtures в PHPUnit для хранилища reset для предотвращения утечек памяти.

Ответ 2

Увеличение памяти имеет три-четыре причины:

1) PHPUnit собирает данные о покрытии кода

С этим ничего не поделаешь, кроме отключения кода.

2) PHPUnit кэширует файловые токены для покрытия кода

Вы можете использовать <phpunit cacheTokens="false"> в своем PHPUnit xml. См. Примечание об этом в http://phpunit.de/manual/current/en/installation.html#install.upgrading.

3) PHPUnit не очищает себя должным образом

В своей текущей реализации он сохраняет контрольные примеры, потому что именно там хранятся данные результатов. В будущем это будет изменено, чтобы быть более эффективным, но пока это как все работает.

4) Ведущий к "4": вам нужно убирать и после себя

Поскольку экземпляры TestCase хранятся вокруг, ваши переменные-члены тоже хранятся.

Это означает, что вы можете сэкономить много памяти, используя

public function tearDown() {
    unset($this->whatever);
}

но делать это очень утомительно.

Я предлагаю иметь базовый класс тестирования для всех ваших тестовых случаев и использовать это:

class MyBaseTest extends \PHPUnit_Framework_TestCase {

    protected function tearDown()
    {
        $refl = new \ReflectionObject($this);
        foreach ($refl->getProperties() as $prop) {
            if (!$prop->isStatic() && 0 !== strpos($prop->getDeclaringClass()->getName(), 'PHPUnit_')) {
                $prop->setAccessible(true);
                $prop->setValue($this, null);
            }
        }
    }
}

Это будет убирать за вами в хорошем, автоматизированном виде.

(реквизиты для фрагмента перейдите по адресу: http://kriswallsmith.net/post/18029585104/faster-phpunit)


PHPUnit не может сделать это обратно совместимым способом, который не нарушил бы проекты людей, поэтому вы должны добавить его для себя :)

Ответ 3

Технические подробности сборки мусора PHPUnit уже были покрыты @edorian и @mauris, но я хотел добавить, что PHPUnit (по крайней мере, в версии 3.7.21, который я запускаю) дает вам возможность добавлять комментарий:

/**
 * @backupGlobals disabled
 */
class MyClassTests extends PHPUnit_Framework_TestCase{}

Прежде чем добавлять аннотацию, я примерно удвоил использование своей памяти в каждом прогоне моего тестового набора, последний из которых достиг 1100 МБ. Теперь они работают на 15 МБ.

Ответ 4

Если вы используете PDO (или аналогичную абстракцию базы данных), вы можете использовать "sqlite:: memory:" как вы DSN. Это имеет три больших преимущества:

  • Автоматическая очистка после каждого теста
  • Невозможно случайно коснуться производственной базы данных (например, даже при выполнении модульных тестов на вашем производственном сервере).
  • Все в памяти, поэтому тесты могут работать быстрее

Недостаток заключается в том, что ваш SQL должен быть переносимым между MySQL и SQLite. Это может оказаться довольно большой работой (для больших проектов это часто стоит не только для тестов, но и для того, как это улучшит ваш дизайн кода.) В вашем случае упоминание использования Doctrine в стенограмме чата, поэтому у вас может быть хорошая абстракция, и поэтому она может запускаться без изменений в SQLite.