Очистить память, используемую PHP
Я столкнулся с интересной проблемой. Я использую PHPUnit, и мои тесты занимают больше памяти каждый раз, когда я их запускаю. Т.е....
2,25 МБ
2,5 МБ
3,0 МБ
3,5 МБ .......
Кто-нибудь знает, как очистить потребляемую память, и может ли кто-нибудь посоветовать мне подробно изучить это? Непосредственная проблема заключается в том, что у некоторых из моих более крупных тестов заканчивается память, и они просто продолжают увеличивать максимальное выделение памяти в PHP недостаточно хорош...Мне нужно знать, почему тест PHPUnit, выполняемый из командной строки, будет использовать память, которая "зависает" между запусками.
4 answers
Кельвин, как обсуждалось в чате , это произошло из-за отсутствия функций сброса.
При тестировании мы должны убедиться, что среда тестирования согласована, чтобы мы могли получать точные результаты. Вычисления - это ввод/вывод, и поэтому мы должны использовать Приспособления в PHPUnit для сброса хранилища, чтобы предотвратить подобные "утечки памяти".
Увеличение объема памяти имеет три-четыре причины:
1) PHPUnit собирает данные о покрытии кода
Вы ничего не можете с этим поделать, кроме как отключить покрытие кода.
2) PHPUnit кэширует маркеры файлов для покрытия кода
Вы можете использовать <phpunit cacheTokens="false">
в своем xml-файле PHPUnit. Смотрите заметку об этом в http://phpunit.de/manual/current/en/installation.html#installing.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 не может сделать это обратно сопоставимым способом, который не нарушил бы проекты людей, поэтому вы должны добавить его для себя:)
Технические детали сборки мусора PHPUnit уже были рассмотрены @edorian и @mauris, но я хотел добавить, что PHPUnit (по крайней мере, в версии 3.7.21, которую я запускаю) дает вам возможность добавить комментарий:
/**
* @backupGlobals disabled
*/
class MyClassTests extends PHPUnit_Framework_TestCase{}
Перед добавлением аннотации я примерно удваивал объем используемой памяти при каждом запуске моего набора тестов, последний из которых составлял около 1100 МБ. Теперь они работают на 15 Мбайт.
Если вы используете PDO (или аналогичную абстракцию базы данных), то вы можете использовать "sqlite::память:" в качестве DSN. Это имеет три больших преимущества:
- Автоматическая очистка после каждого теста
- Нет возможности случайно коснуться производственной базы данных (например, даже при запуске модульных тестов на рабочем сервере).
- Все в памяти, поэтому тесты могут выполняться быстрее
Недостатком является то, что ваш SQL должен быть переносимым между MySQL и SQLite. Это может оказаться будет довольно много работы (для больших проектов это часто того стоит, не только для тестов, но и для того, как это улучшит дизайн вашего кода). В вашем случае вы упоминаете об использовании доктрины в стенограмме чата, так что у вас уже может быть хорошая абстракция, и поэтому она может просто работать без изменений на SQLite.