[PHP extensions and embedded]-memory Management _php Tutorial

Source: Internet
Author: User
Memory management The most important difference between PHP and C is whether to control memory pointers. Inside PHP, setting a string variable is simple: , the string can be freely modified, copied, moved. In C, it is another way, although you can simply initialize it with a static string: char *str = "Hello World"; However, this string cannot be modified because it exists in the code snippet. To create a maintainable string, you need to allocate a chunk of memory and copy the content into it using a function such as strdup (). [CPP] {char *str; str = strdup ("Hello World"), if (!STR) {fprintf (stderr, ' Unable to allocate memory! ');}} Traditional memory management functions (malloc (), free (), StrDup (), realloc (), calloc (), etc.) will not be used directly by PHP's source code, and this chapter will explain why. Frees allocated memory memory management on all previous platforms with request/ Disposed of in the same way. The app tells it to the upper layer (usually the operating system) "I want some memory to use", and if space allows, the operating system provides to the program and makes a record of the provided memory. After the app has used memory, it should return the memory to the OS so that it can be assigned to another place. If the program does not return memory, the OS has no way of knowing that the memory is no longer in use, so it cannot be allocated to other processes. If a piece of memory is not released, and the application that owns it loses its handle, we are called "leaking" because no one can get it directly. In a typical client application, small, infrequent leaks are usually tolerated because the process terminates after a period of time, This leaked memory will be recycled by the OS. It's not that the OS knows the leaked memory, but it knows that the memory allocated for the terminated process will no longer be used. For long-running service-side daemons, including webserver such as Apache, the process is designed to run for a long period of time, usually indefinitely. As a result, the OS cannot interfere with memory usage, and any degree of leakage, no matter how small, can accumulate enough to cause system resources to run out. STRISTR () function considering user space; To find a string that is not case-sensitive, it actually creates a lowercase copy for haystack and needle, and then performs a normal case-sensitive search to find the associated offset. After the offset of the string is positioned, the lowercase versions of the haystack and needle strings are no longer used. If these copies are not released, then each script that uses STRISTR () will leak some memory each time it is called. Eventually, websThe erver process consumes the entire system's memory, but it is not used. The perfect solution is to write good, clean, consistent code that ensures they are absolutely correct. But in an environment like PHP interpreter, this is only half the solution. Error handling in order to provide the ability to jump out of the activation request from the user script and the extension function in place, there is a way to jump out of the entire activation request. The Zend engine is handled by setting a jump-out address where the request begins, after all die ()/exit () calls, or when encountering some critical error (E_error), longjmp () Turn to the pre-set out address. Although this step-out process simplifies the process, it has a problem: resource cleanup code (such as free () calls) is skipped, which can lead to leaks. Consider the code for the following simplified engine handler function call: [cpp] void call_function (const char *fname, int fname_len tsrmls_dc) {zend_function *fe; char *lcase_f Name /* PHP functions are case insensitive, in order to simplify locating them in the function table, all function names are implicitly translated as lowercase */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); When the Php_error_docref () line executes, the internal processor sees that the error level is critical, calls longjmp () interrupts the current program flow, leaves Call_function (), and cannot reach Efree (lcase_fname) line . Then you might want to move the Efree () line to Php_error_docref (), but if the Call_fuThe nction () call enters the first conditional branch (finds the function name, executes normally)? Also, fname is an assigned string, and it is used in the error message and you cannot release it until it is used. The Php_error_docref () function is an internal equivalent to Trigger_error (). The first parameter is an optional document reference, and if enabled in php.ini it will be appended to Docref.root. The third argument can be the severity of any e_* family constant marking errors. The fourth and subsequent parameters are a format string and a variable parameter list that conform to the printf () style. Zend Memory Management The solution for memory leaks due to request jumping out (failure) is the Zend Memory management (ZENDMM) layer. This part of the engine plays the role that the operating system typically plays, allocating memory to the calling application. The difference is that in the cognitive perspective of the process space request, it is sufficiently low level that it can perform the same thing as the OS when the die is requested. This means that it implicitly frees all the memory space that the request has. Shows the relationship between ZENDMM and OS in the PHP process: In addition to providing implicit memory cleanup, ZENDMM also controls the memory usage of each request through PHP.ini settings memory_limit. If the script tries to request more memory than the system allows, or exceeds the amount of the single-process memory limit, ZENDMM automatically raises a E_ERROR message and starts to jump out of the process. An additional benefit is that most of the time the results of memory allocations do not need to be checked, because if the failure is immediately longjmp () jumps out to the end of the engine. The most complex of the hooks between PHP internal code and the OS Real memory management layer is the requirement that all internal memory allocations be selected from a set of functions. For example, allocating a 16-byte block of memory instead of using malloc, the PHP code should use Emalloc (16). In addition to performing a true memory allocation task, ZENDMM also flags information about the request that the memory block binds to, so that the ZENDMM can implicitly release it (allocated memory) when the request fails out. Many times memory needs to be allocated and use more time than the single request life cycle. This allocation, which we call persistent allocations, because they persist after the request has ended, can be allocated using the traditional memory allocator because they cannot be ZENDMM on each requested message. Sometimes, it is only at run time that a particular assignment needs to be persisted or not, so ZENDMM exposes some help macros to replace other memory allocation functions, but adds additional parameters at the end to mark whether or not to holdLong-term. If you really want to persist the allocation, this parameter should be set to 1, in which case the memory allocation request will be passed to the traditional malloc () family allocator. If the runtime logic determines that the block does not need to be persisted, the parameter is set to 0, and the call is directed to the single-request memory allocator function. For example, Pemalloc (Buffer_len, 1) maps to malloc (Buffer_len), and Pemalloc ( Buffer_len, 0) maps to Emalloc (Buffer_len), as follows: [CPP] #define in zend/zend_alloc.h: #define PEMALLOC (size, persistent) \ (( Persistent)? malloc (size): Emalloc (size)) ZENDMM provides a list of allocator functions as follows, and lists their corresponding traditional allocators. The allocator of the traditional allocator in PHP 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 *st Rdup (void *ptr), void *estrdup (void *ptr), void *pestrdup (void *ptr, char persistent), void free (void *ptr), void Efree (void *PTR); void Pefree (void *ptr, char persistent); You may have noticed that Pefree requires a persistent token to be passed. This is because when Pefree () is called, it does not know whether PTR is permanently allocated. Calling free () on a pointer to a spent persistent allocation may result in a double free, while calling on a persisted allocationEfree () usually causes a segment error because the memory manager tries to view management information and it does not exist. Your code needs to remember that the data structure it allocates is not persisted. In addition to the core allocator, ZENDMM also adds special functions: [cpp] void *estrndup (void *ptr, int len); It allocates Len + 1 bytes of memory and copies Len bytes from PTR to the newly allocated block. Estrndup () behaves roughly as follows: [cpp] void *estrndup (void *ptr, int len) {char *dst = emalloc (len + 1); memcpy (DST, PTR, Len); Dst[len] = 0; return DST; The terminating null byte is silently placed at the end of the buffer, which ensures that all functions that use Estrndup () for string assignment do not have to worry about passing the result buffer to a function that expects a null-terminated string (such as printf ()) to produce an error. This last byte will be wasted when copying non-string data using Estrndup (), but it is nothing compared to the convenience it brings. [cpp] void *safe_emalloc (size_t size, size_t count, size_t ADDTL); void *safe_pemalloc (size_t size, size_t count, size_t Addtl, char persistent); The memory size allocated by these two functions is the result of ((size * count) + ADDTL). You might ask, "why expand such a function?" Why not use Emalloc/pemalloc and calculate for yourself? "The reason is that it's called" security. " Although this situation is rarely possible, it is still possible that the results will be bad when the results of the calculation overflow the integer limit on the host platform. It is possible to allocate a negative number of bytes and, worse, allocate a positive memory size, but less than the requested size. Safe_emalloc () avoids this type of trap by checking for an integral type overflow, and it explicitly reports a failure if an overflow occurs. Not all memory allocation routines have p* replicas. For example, Pestrndup () and Safe_pemalloc () do not exist until PHP 5.1. Sometimes you need to work on these deficiencies in ZENDAPI. Reference counting is carefully allocated in a long-running multi-request process such as PHPand freeing up memory is important, but it's only half the job. To make high-concurrency servers more efficient, each request needs to use as little memory as possible, minimizing unnecessary copies of the data. Consider the following PHP code snippet: [PHP] After the first call, a variable is created, which is given a 12-byte block of memory that holds the string "Hello world" and the trailing null. Now for the second sentence: The $b is set to the same value as $ A, and then $ A is unset (released) if PHP believes that each variable assignment requires copying the contents of the variable, an additional 12-byte copy of the duplicated string is required during the copy of the data, as well as additional processor load. When the third line appears, this behavior looks ridiculous, and the original variable is unloaded to make the data copy completely unnecessary. Now let's think about what happens when the contents of a 10MB file are loaded in two variables. It requires 20MB of memory, but 10MB is enough. Does the engine really do this kind of hard work and waste so much time and memory? You know PHP is very smart. Do you remember? In the engine, the variable name and its value are two different concepts. Its value is itself a zval * without a name. Use Zend_hash_add () to assign it to the variable $ A. So are two variable names pointing to the same value? [CPP] {Zval *helloval; Make_std_zval (Helloval); Zval_string (Helloval, "Hello World", 1); Zend_hash_add (EG (active_symbol_table), "a", sizeof ("a"), &helloval, sizeof (zval*), NULL); Zend_hash_add (EG (active_symbol_table), "B", sizeof ("B"), &helloval, sizeof (zval*), NULL); At this point, when you check $ A or $b, you can see that they actually contain the string "Hello world". Unfortunately, then came the third line: unset ($a);. In this case, unset () does not know that the data that a $ A points to is also referenced by another name, which simply frees up memory. Any subsequent access to $b will look at the memory space that has been freed, which will cause the engine to crash. Of course, you don't want the engine to crash. This is solved by Zval's third member: RefCount. When a variable is first created, its refcount is initialized to 1 because we think that only the variable that was created points to it. When your code executes to assign Helloval to $b, it needs to increase the refcount to 2 because the value is now twoA variable "reference" [CPP] {zval *helloval; Make_std_zval (Helloval); Zval_string (Helloval, "Hello World", 1); Zend_hash_add (EG (active_symbol_table), "a", sizeof ("a"), &helloval, sizeof (zval*), NULL); Zval_addref (Helloval); Zend_hash_add (EG (active_symbol_table), "B", sizeof ("B"), &helloval, sizeof (zval*), NULL); Now, when unset () deletes a $ A copy of a variable, it sees through refcount that someone else is interested in the data, so it just reduces the refcount by 1 and does nothing else. Write-time replication by reference counting saving memory is a good idea, But what do you do when you just want to modify one of the variables? Consider the following code snippet: [PHP] Look at the logic of the above code, after processing the expected $ A is still equal to 1, and $b equals 6. Now you know, Zend in order to maximize memory savings, after the second line of code executes $ A and $b only want the same zval, so what happens when we get to the third line of code? Will $b be modified? The answer is Zend view refcount, and see that it is greater than 1, isolating it. The isolation in the Zend engine is to break a reference pair, which is antagonistic to the processing you just saw: [CPP] Zval *get_var_and_separate (char *varname, int varname_len tsrmls_dc) {zval * * Varval, *varcopy; if (Zend_hash_find (EG (active_symbol_table), varname, Varname_len + 1, (void**) &varval) = = FAILURE) {/* variable does not exist */retur n NULL; if ((*varval)->refcount < 2) {/* variable name has only one reference, do not need to isolate */return *varval;}/* Other cases, make a shallow copy of Zval * */make_std_zval (VA Rcopy); Varcopy = *varval; /* Make a deep copy of Zval * */Zval_copy_ctor (varcopy); /* Destroys the relationship between VarName and Varval, which reduces the reference count of Varval by 1 */Zend_hash_del (EG (active_symbol_table), varname, Varname_len + 1); /* Initializes the reference count for the newly created value and associates the newly created value with the varname */varcopy->refcount = 1; Varcopy->is_ref = 0; Zend_hash_add (EG (active_symbol_table), varname, Varname_len + 1, &varcopy, sizeof (zval*), NULL); /* Return the new zval * */return varcopy; Now the engine has a zval * that is only referenced by the $b variable, it can be converted to a long and its value is requested by the scriptAdded 5. The concept of modifying reference counts while writing also creates a new way of maintaining data, which is referred to as a "reference" by user-space scripting. Consider the following user space code snippet: [PHP] With your experience with PHP, intuitively you may realize that a value of $ A should now be 6, even if it is initialized to 1 and has not been (directly) modified. This happens because when the engine increases the value of $b by 5, it notices that $b is a reference to $ A, and it says "it is no problem for me to modify the value without isolating it, because I would have wanted all the reference variables to see the change" but how does the engine know? Very simply, it looks at the last element of the ZVAL structure: is_ref. It's just a simple switch that defines whether the zval is a value or a reference in user space. In the preceding code fragment, after the first line is executed, the Zval created for $ A, RefCount is 1, and Is_ref is 0 because it belongs to only one variable ($a), and no other variable references point to it. When the second line executes, the refcount of this zval increases to 2, but at this point, because the script adds a fetch address character (&) to mark it as a reference value, the Is_ref is set to 1. Finally, in the third row, the engine obtains the zval of the $b association and checks whether isolation is required. At this point the zval will not be quarantined because we do not have a piece of code in front of it (below). There is another condition for checking refcount in Get_var_and_separate (): [CPP if ((*varval)->is_ref | | | (*varval)->refcount < 2) {/* VarName does not occur only if it is really referenced, or if it is only referenced by a variable */return *varval; At this time, even if the refcount is 2, the isolation process will be shorted , because this value is a reference value. The engine is free to modify it without worrying about other variables that reference it. Isolation problems for these copies and references, there are some combinations that is_ref and refcount do not handle well. Consider the following code: [PHP] Here you have a value that needs to be associated with 3 different variables, two are reference methods that are modified at write time, and the other is the isolated write-time context. How do you describe this relationship using only Is_ref and refcount? The answer is: No. In this case, the value must be copied to two separate zval *, although both contain the same data. For example, the following code block will cause the same conflict and force the value to be quarantined in a copy (e.g.) [PHP] Note that in both cases, the $b is associated with the original Zval object because the engine does not know the name of the third variable involved in the operation when the isolation occurs. Summary PHP is a managed language. From the side of the user space, careful control of resources and memory means easier prototyping involving and fewer crashes. After you delve into the unveiling of the engine, you can no longer have a mind, but are responsible for the development and maintenance of operational environmental integrity.

http://www.bkjia.com/PHPjc/477795.html www.bkjia.com true http://www.bkjia.com/PHPjc/477795.html techarticle Memory Management PHP and C The most important difference is whether to control the memory pointer. Inside PHP, setting a string variable is simple: php $str = Hello world;?, string can be freely modified ...

  • Contact Us

    The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

    If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

    A Free Trial That Lets You Build Big!

    Start building with 50+ products and up to 12 months usage for Elastic Compute Service

    • Sales Support

      1 on 1 presale consultation

    • After-Sales Support

      24/7 Technical Support 6 Free Tickets per Quarter Faster Response

    • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.