php教程 php手册 解析PHP中的内存管理,PHP动态分配和释放内存

解析PHP中的内存管理,PHP动态分配和释放内存

Jun 06, 2016 pm 08:29 PM
메모리 관리 여유 메모리

本篇文章是对PHP中的内存管理,PHP动态分配和释放内存进行了详细的分析介绍,需要的朋友参考下

摘要 内存管理对于长期运行的程序,例如服务器守护程序,是相当重要的影响;因此,理解PHP是如何分配与释放内存的对于创建这类程序极为重要。本文将重点探讨PHP的内存管理问题。

一、 内存
在PHP中,填充一个字符串变量相当简单,这只需要一个语句"<?php $str = 'hello world '; ?>"即可,并且该字符串能够被自由地修改、拷贝和移动。而在C语言中,尽管你能够编写例如"char *str = "hello world ";"这样的一个简单的静态字符串;但是,却不能修改该字符串,因为它生存于程序空间内。为了创建一个可操纵的字符串,你必须分配一个内存块,并且通过一个函数(例如strdup())来复制其内容。

复制代码 代码如下:


{
 char *str;
 str = strdup("hello world");
 if (!str) {
fprintf(stderr, "Unable to allocate memory!");
 }
}


由于后面我们将分析的各种原因,传统型内存管理函数(例如malloc(),free(),strdup(),realloc(),calloc(),等等)几乎都不能直接为PHP源代码所使用。

二、 释放内存
在几乎所有的平台上,内存管理都是通过一种请求和释放模式实现的。首先,一个应用程序请求它下面的层(通常指"操作系统"):"我想使用一些内存空间"。如果存在可用的空间,操作系统就会把它提供给该程序并且打上一个标记以便不会再把这部分内存分配给其它程序。

当应用程序使用完这部分内存,它应该被返回到OS;这样以来,免备案空间,它就能够被继续分配给其它程序。如果该程序不返回这部分内存,那么OS无法知道是否这块内存不再使用并进而再分配给另一个进程。如果一个内存块没有释放,并且所有者应用程序丢失了它,那么,我们就说此应用程序"存在漏洞",网站空间,因为这部分内存无法再为其它程序可用。

在一个典型的客户端应用程序中,较小的不太经常的内存泄漏有时能够为OS所"容忍",因为在这个进程稍后结束时该泄漏内存会被隐式返回到OS。这并没有什么,因为OS知道它把该内存分配给了哪个程序,并且它能够确信当该程序终止时不再需要该内存。

而对于长时间运行的服务器守护程序,包括象Apache这样的web服务器和扩展php模块来说,进程往往被设计为相当长时间一直运行。因为OS不能清理内存使用,所以,任何程序的泄漏-无论是多么小-都将导致重复操作并最终耗尽所有的系统资源。

现在,我们不妨考虑用户空间内的stristr()函数;为了使用大小写不敏感的搜索来查找一个字符串,它实际上创建了两个串的各自的一个小型副本,然后执行一个更传统型的大小写敏感的搜索来查找相对的偏移量。然而,在定位该字符串的偏移量之后,它不再使用这些小写版本的字符串。如果它不释放这些副本,那么,每一个使用stristr()的脚本在每次调用它时都将泄漏一些内存。最后,web服务器进程将拥有所有的系统内存,但却不能够使用它。

你可以理直气壮地说,理想的解决方案就是编写良好、干净的、一致的代码。这当然不错;但是,在一个象PHP解释器这样的环境中,这种观点仅对了一半。

三、 错误处理
为了实现"跳出"对用户空间脚本及其依赖的扩展函数的一个活动请求,需要使用一种方法来完全"跳出"一个活动请求。这是在Zend引擎内实现的:在一个请求的开始设置一个"跳出"地址,然后在任何die()或exit()调用或在遇到任何关键错误(E_ERROR)时执行一个longjmp()以跳转到该"跳出"地址。

尽管这个"跳出"进程能够简化程序执行的流程,但是,在绝大多数情况下,这会意味着将会跳过资源清除代码部分(例如free()调用)并最终导致出现内存漏洞。现在,让我们来考虑下面这个简化版本的处理函数调用的引擎代码:

复制代码 代码如下:


void call_function(const char *fname, int fname_len TSRMLS_DC){
 zend_function *fe;
 char *lcase_fname;
 /* PHP函数名是大小写不敏感的,
 *为了简化在函数表中对它们的定位,
 *所有函数名都隐含地翻译为小写的
 */
 lcase_fname = estrndup(fname, fname_len);
 zend_str_tolower(lcase_fname, fname_len);
 if (zend_hash_find(EG(function_table),lcase_fname, fname_len + 1, (void **)&fe) == FAILURE) {
zend_execute(fe->op_array TSRMLS_CC);
 } else {
php_error_docref(NULL TSRMLS_CC, E_ERROR,"Call to undefined function: %s()", fname);
 }
 efree(lcase_fname);
}


当执行到php_error_docref()这一行时,内部错误处理器就会明白该错误级别是critical,并相应地调用longjmp()来中断当前程序流程并离开call_function()函数,甚至根本不会执行到efree(lcase_fname)这一行。你可能想把efree()代码行移动到zend_error()代码行的上面;但是,调用这个call_function()例程的代码行会怎么样呢?fname本身很可能就是一个分配的字符串,并且,在它被错误消息处理使用完之前,你根本不能释放它。

注意,这个php_error_docref()函数是trigger_error()函数的一个内部等价实现。它的第一个参数是一个将被添加到docref的可选的文档引用。第三个参数可以是任何我们熟悉的E_*家族常量,用于指示错误的严重程度。第四个参数(最后一个)遵循printf()风格的格式化和变量参数列表式样。

四、 Zend内存管理器
在上面的"跳出"请求期间解决内存泄漏的方案之一是:使用Zend内存管理(ZendMM)层。引擎的这一部分非常类似于操作系统的内存管理行为-分配内存给调用程序。区别在于,它处于进程空间中非常低的位置而且是"请求感知"的;这样以来,当一个请求结束时,它能够执行与OS在一个进程终止时相同的行为。也就是说,它会隐式地释放所有的为该请求所占用的内存。图1展示了ZendMM与OS以及PHP进程之间的关系。

深入探讨PHP中的内存管理问题


图1.Zend内存管理器代替系统调用来实现针对每一种请求的内存分配。

除了提供隐式内存清除功能之外,ZendMM还能够根据php.ini中memory_limit的设置控制每一种内存请求的用法。如果一个脚本试图请求比系统中可用内存更多的内存,或大于它每次应该请求的最大量,那么,ZendMM将自动地发出一个E_ERROR消息并且启动相应的"跳出"进程。这种方法的一个额外优点在于,大多数内存分配调用的返回值并不需要检查,因为如果失败的话将会导致立即跳转到引擎的退出部分。

把PHP内部代码和OS的实际的内存管理层"钩"在一起的原理并不复杂:所有内部分配的内存都要使用一组特定的可选函数实现。例如,PHP代码不是使用malloc(16)来分配一个16字节内存块而是使用了emalloc(16)。除了实现实际的内存分配任务外,ZendMM还会使用相应的绑定请求类型来标志该内存块;这样以来,当一个请求"跳出"时,ZendMM可以隐式地释放它。

经常情况下,内存一般都需要被分配比单个请求持续时间更长的一段时间。这种类型的分配(因其在一次请求结束之后仍然存在而被称为"永久性分配"),可以使用传统型内存分配器来实现,因为这些分配并不会添加ZendMM使用的那些额外的相应于每种请求的信息。然而有时,直到运行时刻才会确定是否一个特定的分配需要永久性分配,因此ZendMM导出了一组帮助宏,其行为类似于其它的内存分配函数,但是使用最后一个额外参数来指示是否为永久性分配。

如果你确实想实现一个永久性分配,那么这个参数应该被设置为1;在这种情况下,请求是通过传统型malloc()分配器家族进行传递的。然而,如果运行时刻逻辑认为这个块不需要永久性分配;那么,这个参数可以被设置为零,并且调用将会被调整到针对每种请求的内存分配器函数。

例如,pemalloc(buffer_len,1)将映射到malloc(buffer_len),而pemalloc(buffer_len,0)将被使用下列语句映射到emalloc(buffer_len):
#define in Zend/zend_alloc.h:
#define pemalloc(size, persistent) ((persistent)?malloc(size): emalloc(size))

所有这些在ZendMM中提供的分配器函数都能够从下表中找到其更传统的对应实现。
表格1展示了ZendMM支持下的每一个分配器函数以及它们的e/pe对应实现:
表格1.传统型相对于PHP特定的分配器。

分配器函数 e/pe对应实现

void *malloc(size_t count); void *emalloc(size_t count);void *pemalloc(size_t count,char persistent);

void *calloc(size_t count); void *ecalloc(size_t count);void *pecalloc(size_t count,char persistent);

void *realloc(void *ptr,size_t count); void *erealloc(void *ptr,size_t count);
void *perealloc(void *ptr,size_t count,char persistent);

void *strdup(void *ptr); void *estrdup(void *ptr);void *pestrdup(void *ptr,char persistent);

본 웹사이트의 성명
본 글의 내용은 네티즌들의 자발적인 기여로 작성되었으며, 저작권은 원저작자에게 있습니다. 본 사이트는 이에 상응하는 법적 책임을 지지 않습니다. 표절이나 침해가 의심되는 콘텐츠를 발견한 경우 admin@php.cn으로 문의하세요.

핫 AI 도구

Undresser.AI Undress

Undresser.AI Undress

사실적인 누드 사진을 만들기 위한 AI 기반 앱

AI Clothes Remover

AI Clothes Remover

사진에서 옷을 제거하는 온라인 AI 도구입니다.

Undress AI Tool

Undress AI Tool

무료로 이미지를 벗다

Clothoff.io

Clothoff.io

AI 옷 제거제

Video Face Swap

Video Face Swap

완전히 무료인 AI 얼굴 교환 도구를 사용하여 모든 비디오의 얼굴을 쉽게 바꾸세요!

뜨거운 도구

메모장++7.3.1

메모장++7.3.1

사용하기 쉬운 무료 코드 편집기

SublimeText3 중국어 버전

SublimeText3 중국어 버전

중국어 버전, 사용하기 매우 쉽습니다.

스튜디오 13.0.1 보내기

스튜디오 13.0.1 보내기

강력한 PHP 통합 개발 환경

드림위버 CS6

드림위버 CS6

시각적 웹 개발 도구

SublimeText3 Mac 버전

SublimeText3 Mac 버전

신 수준의 코드 편집 소프트웨어(SublimeText3)

C++ 객체 레이아웃은 메모리에 맞춰 정렬되어 메모리 사용 효율성을 최적화합니다. C++ 객체 레이아웃은 메모리에 맞춰 정렬되어 메모리 사용 효율성을 최적화합니다. Jun 05, 2024 pm 01:02 PM

C++ 개체 레이아웃 및 메모리 정렬은 메모리 사용 효율성을 최적화합니다. 개체 레이아웃: 데이터 멤버가 선언된 순서대로 저장되어 공간 활용을 최적화합니다. 메모리 정렬: 액세스 속도를 향상시키기 위해 데이터를 메모리에 정렬합니다. alignas 키워드는 캐시 라인 액세스 효율성을 향상시키기 위해 64바이트 정렬된 CacheLine 구조와 같은 사용자 정의 정렬을 지정합니다.

C++ 메모리 관리: 사용자 정의 메모리 할당자 C++ 메모리 관리: 사용자 정의 메모리 할당자 May 03, 2024 pm 02:39 PM

C++의 사용자 정의 메모리 할당자를 사용하면 개발자가 필요에 따라 메모리 할당 동작을 조정할 수 있습니다. 사용자 정의 할당자를 생성하려면 std::allocator를 상속하고 할당() 및 할당 해제() 함수를 다시 작성해야 합니다. 실제적인 예로는 성능 향상, 메모리 사용 최적화, 특정 동작 구현 등이 있습니다. 사용할 때에는 메모리 해제 방지, 메모리 정렬 관리, 벤치마크 테스트 수행에 주의가 필요합니다.

대규모 코드 기반의 C++ 함수 메모리 할당 및 소멸에 대한 모범 사례 대규모 코드 기반의 C++ 함수 메모리 할당 및 소멸에 대한 모범 사례 Apr 22, 2024 am 11:09 AM

C++ 함수 메모리 할당 및 소멸에 대한 모범 사례에는 정적 메모리 할당을 위한 로컬 변수 사용이 포함됩니다. 동적 메모리 할당에는 스마트 포인터를 사용하십시오. 메모리는 생성자에서 할당되고 소멸자에서 소멸됩니다. 복잡한 메모리 시나리오에는 사용자 지정 메모리 관리자를 사용하세요. 예외가 발생할 때 할당된 메모리가 해제되도록 리소스 정리에 예외 처리를 사용합니다.

C++ 함수 메모리 할당 및 소멸을 위한 확장 및 고급 기술 C++ 함수 메모리 할당 및 소멸을 위한 확장 및 고급 기술 Apr 22, 2024 pm 05:21 PM

C++ 함수 메모리 관리는 다음을 포함한 확장 및 고급 기술을 제공합니다. 사용자 정의 할당자: 사용자가 자신의 메모리 할당 전략을 정의할 수 있습니다. Placementnew 및 Placementdelete: 객체를 특정 메모리 위치에 할당해야 할 때 사용됩니다. 고급 기술: 메모리 누수를 줄이고 성능을 개선하며 코드를 단순화하는 메모리 풀, 스마트 포인터 및 RAII.

멀티스레드 환경에서 C++ 메모리 관리의 과제와 대책은? 멀티스레드 환경에서 C++ 메모리 관리의 과제와 대책은? Jun 05, 2024 pm 01:08 PM

다중 스레드 환경에서 C++ 메모리 관리는 데이터 경합, 교착 상태 및 메모리 누수와 같은 문제에 직면합니다. 대책에는 다음이 포함됩니다. 1. 뮤텍스 및 원자 변수와 같은 동기화 메커니즘을 사용합니다. 2. 잠금 없는 데이터 구조를 사용합니다. 3. 스마트 포인터를 사용합니다. 4. (선택 사항) 가비지 수집을 구현합니다.

golang 함수 및 goroutine의 메모리 관리 golang 함수 및 goroutine의 메모리 관리 Apr 25, 2024 pm 03:57 PM

Go의 함수에 대한 메모리는 값으로 전달되며 원래 변수에 영향을 주지 않습니다. 고루틴은 메모리를 공유하며 할당된 메모리는 고루틴이 실행을 완료할 때까지 GC에서 회수되지 않습니다. 완성된 고루틴 참조를 유지하거나, 전역 변수를 사용하거나, 정적 변수를 피하면 메모리 누수가 발생할 수 있습니다. 누출을 방지하려면 채널을 통해 고루틴을 취소하고, 정적 변수를 피하고, defer 문을 사용하여 리소스를 해제하는 것이 좋습니다.

C++ 메모리 관리는 운영 체제 및 가상 메모리와 어떻게 상호 작용합니까? C++ 메모리 관리는 운영 체제 및 가상 메모리와 어떻게 상호 작용합니까? Jun 02, 2024 pm 09:03 PM

C++ 메모리 관리는 운영 체제와 상호 작용하고 운영 체제를 통해 실제 메모리와 가상 메모리를 관리하며 프로그램에 메모리를 효율적으로 할당 및 해제합니다. 운영 체제는 물리적 메모리를 페이지로 나누고 필요에 따라 가상 메모리에서 애플리케이션이 요청한 페이지를 가져옵니다. C++에서는 new 및 delete 연산자를 사용하여 메모리를 할당 및 해제하고 운영 체제에 메모리 페이지를 요청하고 이를 각각 반환합니다. 운영 체제는 실제 메모리를 해제할 때 덜 사용된 메모리 페이지를 가상 메모리로 교체합니다.

PHP 함수에서 메모리 사용량을 관리하는 방법은 무엇입니까? PHP 함수에서 메모리 사용량을 관리하는 방법은 무엇입니까? Apr 26, 2024 pm 12:12 PM

PHP 함수에서 메모리 사용량을 관리하려면, 불필요한 변수 선언을 피하고, 사용하지 않는 변수를 해제하고, 무한 루프 방지 및 인덱스 배열 사용과 같은 제한 함수 매개변수를 최적화하세요.

See all articles