Maison >développement back-end >tutoriel php >Gestionnaire de mémoire Zend pour php
Zend Memory Manager, souvent abrégé en ZendMM ou ZMM, est une couche C conçu pour offrir la possibilité d'allouer et de libérer de la mémoire dynamique liée aux requêtes .
Faites attention à « demande de liaison » dans la phrase ci-dessus.
ZendMM n'est pas simplement une couche classique au-dessus de l'allocateur de mémoire dynamique de la libc, principalement représentée par deux appels API malloc()/free()
. ZendMM concerne la mémoire liée aux requêtes que PHP doit allouer lors du traitement d'une requête.
Recommandations d'apprentissage associées : Programmation PHP de l'entrée à la maîtrise
PHP C'est une architecture du rien partagé. Eh bien, pas à 100 %. Nous vous expliquons plus d'informations sur les étapes et les cycles.
ZendMM est livré avec une API qui remplace l'allocateur dynamique de la libc en copiant son API. Les programmeurs doivent utiliser cette API au lieu de l'allocateur de la libc lors du traitement des requêtes. Par exemple, lorsque PHP traite une requête, il analysera le fichier PHP. Par exemple, celles-ci donneront lieu à des déclarations de fonctions et de classes. Lorsque le compilateur commence à compiler un fichier PHP, il alloue de la mémoire dynamique pour stocker les classes et fonctions qu'il découvre. Cependant, PHP les publie à la fin de la requête. Par défaut, PHP oubliePHP peut gérer des centaines ou des milliers de requêtes dans le même processus. Par défaut, PHP oubliera toute information sur la requête en cours une fois celle-ci terminée.
Le message "oublier" est interprété comme libérant les tampons dynamiques alloués lors du traitement de la requête. Cela signifie que vous ne pouvez pas utiliser les appels libc traditionnels pour allouer de la mémoire dynamique lors du traitement d'une requête. C'est parfaitement valable, mais vous vous donnez la possibilité d'oublier de libérer le buffer.
beaucoup d'
informations d'une requête à l'autre. Cependant, il existe des informations très rares dont vous avez besoin pour persister dans plusieurs requêtes. Mais ce n'est pas courant.Qu'est-ce qui peut rester inchangé par les demandes ? Ce que nous appelons un objet persistant
. Encore une fois : ce n’est pas une situation courante. Par exemple, le chemin actuel de l'exécutable PHP ne change pas entre les requêtes. Ses informations sont allouées de manière permanente, ce qui signifie qu'il appelle les libc traditionnellespour les allouer.
Quoi d'autre ? une certaine ficelle. Par exemple, la chaîne "_SERVER" sera réutilisée entre les requêtes car un tableau malloc ()
PHP sera créé pour chaque requête. Ainsi, la chaîne
elle-même peut être allouée de manière permanente, puisqu'elle ne sera allouée qu'une seule fois. Vous devez vous rappeler :$_SERVER
Allocation dynamique permanente.
Ne doit être effectué qu'à l'aide de l'API d'allocation dynamique de mémoire ZendMM.
Ne sera pas suivi par ZendMM et vous ne serez pas averti des fuites.
zend_string *foo = zend_string_init("foo", strlen("foo"), 0);Il s'agit d'une allocation persistante :
zend_string *foo = zend_string_init("foo", strlen("foo"), 1);La même HashTable.
Demande d'allocation liée :
zend_array ar; zend_hash_init(&ar, 8, NULL, NULL, 0);
Allocation persistante :
zend_array ar; zend_hash_init(&ar, 8, NULL, NULL, 1);
C'est toujours la même dans toutes les différentes API Zend. Généralement passé en dernier paramètre,
"0"
"1"
signifie "Je veux utiliser ZendMM pour allouer cette structure, donc la liaison est demandée", ou"1" signifie "Je veux utiliser ZendMM pour allouer cette structure, donc la liaison est demandée" Appelez les libc traditionnelles pour allouer cette structure ". Apparemment, ces structures fournissent une API qui se souvient de la façon dont elles ont alloué la structure afin que la fonction de désallocation correcte soit utilisée lors de sa destruction. Donc dans un code comme celui-ci : malloc()
zend_string_release(foo); zend_hash_destroy(&ar);API sait si ces structures sont allouées à l'aide de la liaison de requête ou allouées de manière permanente, le premier cas utilisera
pour la libérer, le deuxième cas la libc
.L'API se trouve dans Zend/zend_alloc.h
L'API est principalement constituée de macros C, pas de fonctions, donc si vous les déboguez et souhaitez comprendre leur fonctionnement Principe, soyez prêt. Ces API copient les fonctions de la libc, souvent avec un « e » ajouté au nom de la fonction ; vous ne devriez donc pas commettre d'erreur, il n'y a pas beaucoup de détails sur l'API ;
En gros, ceux que vous utiliserez le plus souvent sont emalloc(size_t)
et efree(void *)
.
fournit également ecalloc(size_t nmemb,size_t size)
, qui alloue un seul size
de taille nmemb
et met à zéro la région. Si vous êtes un programmeur C expérimenté, sachez que dans la mesure du possible, il est préférable d'utiliser emalloc()
plutôt que ecalloc()
car ecalloc()
mettra à zéro la zone mémoire, ce qui est important dans la détection des erreurs de pointeur. aide. Gardez à l'esprit que emalloc()
fonctionne fondamentalement de la même manière que la libc malloc()
: elle recherchera une zone suffisamment grande dans différentes piscines et vous donnera l'espace le plus approprié. Par conséquent, vous risquez de vous retrouver avec un pointeur récupéré.
puis safe_emalloc(size_t nmemb,size_t size,size_t offset)
, qui est emalloc(size * nmemb + offset)
, mais il vérifie le débordement pour vous. Cet appel API doit être utilisé si le numéro qui doit être fourni provient d'une source non fiable (telle que l'espace utilisateur).
Concernant les chaînes, estrdup(char *)
et estrndup(char *, size_t len)
permettent de copier des chaînes ou des chaînes binaires.
Peu importe ce qui arrive, le pointeur renvoyé par ZendMM doit être libéré en appelant le efree()
de ZendMM, et n'est pas le free() de la libc.
Remarque
sur l'allocation persistante. Les allocations durables restent valables entre les demandes. Vous utilisez généralement la libc habituelle
malloc/ free
pour ce faire, mais ZendMM a quelques raccourcis pour l'allocateur libc : l'API "persistante". L'API commence par des lettres "p" et vous permet de choisir entre une allocation ZendMM ou une allocation persistante. Par conséquent,pemalloc(size_t, 1)
est justemalloc()
,pefree(void *, 1)
estfree()
etpestrdup(void *, 1)
eststrdup()
. Je dis juste.
ZendMM fournit les fonctionnalités suivantes :
ZendMM est un espace utilisateur PHP sous-jacent au " fonction "limite_mémoire". Chaque octet alloué à l'aide de la couche ZendMM est compté et additionné. Vous savez ce qui se passe lorsque la memory_limit de l'INI est atteinte. Cela signifie également que toutes les allocations effectuées via ZendMM sont reflétées dans memory_get_usage()
dans l'espace utilisateur PHP.
En tant que développeur d'extensions, c'est une bonne chose car cela permet de suivre la taille du tas de votre processus PHP.
Si une erreur de limite de mémoire est initiée, le moteur sortira de la position actuelle du code dans le bloc de capture, puis se terminera gracieusement. Mais il est impossible de revenir à un emplacement de code dépassant la limite. Vous devez être préparé à cela.
Théoriquement, cela signifie que ZendMM ne peut pas vous renvoyer un pointeur NULL. Si l'allocation du système d'exploitation échoue ou si l'allocation produit une erreur de limite de mémoire, le code s'exécutera dans un bloc catch et ne reviendra pas à votre appel d'allocation.
Si pour une raison quelconque vous devez contourner cette protection, vous devez utiliser les appels libc traditionnels, tels que malloc()
. Dans tous les cas, soyez prudent et sachez ce que vous faites. Si vous utilisez ZendMM, vous devrez peut-être allouer une grande quantité de mémoire et dépasser la memory_limit de PHP. Utilisez donc un autre allocateur (comme la libc), mais attention : votre extension augmentera la taille actuelle du tas du processus. memory_get_usage()
n'est pas visible en PHP, mais le tas actuel peut être analysé à l'aide des fonctionnalités du système d'exploitation (telles que /proc/{pid}/maps)
Remarque
Si vous devez désactiver complètement ZendMM, vous pouvez démarrer PHP en utilisant la variable d'environnement
USE_ZEND_ALLOC = 0
. De cette façon, chaque appel à l'API ZendMM (comme emalloc()) sera dirigé vers l'appel libc et ZendMM sera désactivé. Ceci est particulièrement utile dans le cas du débogage de la mémoire.
Rappelez-vous la règle principale de ZendMM : il démarre lorsque la requête démarre, puis s'attend à ce que vous appeliez son API lorsque vous avez besoin de mémoire dynamique pour gérer la requête. Lorsque la requête en cours se termine, ZendMM s'arrête.
En fermant, il parcourra tous ses pointeurs en direct et si vous utilisez la version de débogage de PHP, il vous avertira des fuites de mémoire.
Expliquons-le un peu plus clairement : Si à la fin de la requête en cours, ZendMM trouve des blocs mémoire actifs, cela signifie que ces blocs mémoire fuient. Il ne devrait y avoir aucun bloc de mémoire actif sur le tas ZendMM à la fin de la requête, car celui qui a alloué de la mémoire aurait dû la libérer.
如果您忘记释放块,它们将全部显示在 stderr上。此内存泄漏报告进程仅在以下情况下有效:
这是一个简单泄漏到扩展中的示例:
PHP_RINIT_FUNCTION(example) { void *foo = emalloc(128); }
在启动该扩展的情况下启动 PHP,在调试版本上会在 stderr 上生成:
[Fri Jun 9 16:04:59 2017] Script: '/tmp/foobar.php' /path/to/extension/file.c(123) : Freeing 0x00007fffeee65000 (128 bytes), script=/tmp/foobar.php === Total 1 memory leaks detected ===
当 Zend 内存管理器关闭时,在每个已处理请求的末尾,将生成这些行。
但是要当心:
你必须记住的是 ZendMM 泄漏跟踪是一个不错的奖励工具,但它不能代替真正的 C 内存调试器。
这是使用 ZendMM 时最常见的错误,以及你应该怎么做。
获取有关 PHP 生命周期的信息,以了解在扩展中何时处理请求,何时不处理。如果在请求范围之外使用 ZendMM(例如在MINIT()中),在处理第一个请求之前,ZendMM 会静默清除分配,并且可能会使用after-after-free:根本没有。
使用内存调试器。如果你在 ZendMM 返回的内存区域以下或过去写入内容,则将覆盖关键的 ZendMM 结构并触发崩溃。如果 ZendMM 能够为你检测到混乱,则可能会显示“zend_mm_heap损坏”的消息。堆栈追踪将显示从某些代码到某些 ZendMM 代码的崩溃。ZendMM 代码不会自行崩溃。如果你在 ZendMM 代码中间崩溃,那很可能意味着你在某个地方弄乱了指针。插入你喜欢的内存调试器,查找有罪的部分并进行修复。
如果分配一个 ZendMM 指针(即emalloc()
)并使用 libc 释放它(free()
),或相反的情况:你将崩溃。要严谨对待。另外,如果你将其不知道的任何指针传递给 ZendMM 的efree()
:将会崩溃。
Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!