PHP 内存管理 写时复制 垃圾回收

PHP中的变量是不需要手动释放的,内核帮我们实现了变量的内存管理,包括内存的分配和回收

变量深拷贝带来的问题就是效率和内存浪费严重。

解决深拷贝:1、引用计数 2、写时复制

PHP变量的内存管理就是基于这两点实现的

当变量赋值、引用的时候不是进行深拷贝,而是多个变量共用一个value,引用计数来记录这个变量被引用过多少次,当其中的一个变量发生变化时将无法与其他的变量共用

value时,这个时候就需要进行深拷贝进行分离value,这就是写时复制。

引用计数:

用来记录当前有多少个zval指向同一个zend_value,当有新的zval指向这个value时,计数器+1,当zval销毁时,计数书-1,当计数器为0,value就释放来。

PHP7的引用计数保存在了zend_value中,成员gc用来保存引用计数的。PHP中的局部变量zavl分配在zend_execute_data结构上,也就是我们调试的execute_ex函数中的

execute_data变量,它是运行期间最重要,最关键的一个结构,用于局部变量的分配、保存执行位置、调用上下文切换等。

写时复制:

它只有在有必要的时候即发生写的时候才进行深拷贝,可以很好的提高效率,例如Linux操作系统中fork子进程时并不会立即复制父进程的地址空间,而是让父子进程共享一个内存空间,只有在需要写入的时候才会复制地址空间,从而使各个进程拥有各自独立的地址空间,也就是说资源的复制是在需要写入的时候才会进行,在此之前,以只读方式共享。也并不是所有的类型都可以进行复制,对象资源就无法进行复制。

自动GC回收:

在zval断开value的执行是如果发现refounct=0则会直接释放value,这就是变量的回收时机,发生断开的两种常用情况为修改变量与函数返回时,修改变量是会断开value的指向,函数返回时会释放所有的变量。使用unset()函数也可以主动销毁一个变量

垃圾回收:

通过引用计数实现了变量的自动GC机制,但是有一种情况是这个机制无法解决的,从而因变量无法回收导致内存始终得不到释放,造成内存泄漏。这种情况就是循环引用,循环引用就是引用了自身导致无法释放,比如数组中元素引用了这个数组。因为这种因为循环引用而导致无法释放的变量称之为垃圾,PHP引用了另一种机制来对这些垃圾进行回收,也就是垃圾回收器:

这里要明确,如果一个变量value的refcount减少到0,value释放,不属于垃圾。如果一个变量的refcount减少之后大于0,那这可能是垃圾,垃圾回收器会把这种变量收集起来,垃圾回收器会把这种垃圾保存到一个buffer缓存区,等达到一定数量后就会启动垃圾鉴定回收程序,回收算法原理其实很简单,既然垃圾回收是因为引用自身引起的啊,那我就把value的refcount减1,如果返回是0就说明是垃圾,否则再把减1恢复,说明是正常变量。

原文地址:https://www.cnblogs.com/weiluoyan/p/8858624.html

时间: 2024-08-03 18:40:44

PHP 内存管理 写时复制 垃圾回收的相关文章

php 垃圾回收机制----写时复制和引用计数

PHP使用引用计数和写时复制来管理内存.写时复制保证了变量间复制值不浪费内存,引用计数保证了当变量不再需要时,将内存释放给操作系统. 要理解PHP内存管理,首先要理解一个概念----符号表. 符号表的概念: 一个变量有两部分组成:变量名和变量值.而符号表就是将变量名映射到内存中变量值所在地址的数组. 写时复制: 当一个变量的值复制到另一个变量时,PHP没有为复制值使用更多的内存.相反,他会跟新符号表来说明这两个变量拥有相同的内存块.所以下面的代码实际上并没有创建新数组: <?php $peopl

Linux进程管理——fork()和写时复制

写时复制技术最初产生于Unix系统,用于实现一种傻瓜式的进程创建:当发出fork(  )系统调用时,内核原样复制父进程的整个地址空间并把复制的那一份分配给子进程.这种行为是非常耗时的,因为它需要: ·      为子进程的页表分配页面 ·      为子进程的页分配页面 ·      初始化子进程的页表 ·      把父进程的页复制到子进程相应的页中 创建一个地址空间的这种方法涉及许多内存访问,消耗许多CPU周期,并且完全破坏了高速缓存中的内容.在大多数情况下,这样做常常是毫无意义的,因为许多

Ring3下绕过Windows写时复制机制实现全局EAT钩子

在注入到某进程中对Ntdll下EAT钩子的时候作用域仅仅只是当前进程,可是明明所有进程的Ntdll模块全是映射的同一个啊.原来Windows支持一种机制,允许两个或两个以上的进程共享同一块存储器.不过操作系统会给共享的存储页指定写时复制属性,当有个进程想修改一个共享页面时,操作系统会从内存中找到一个闲置页面并将修改的页面内容复制到这个闲置页面上,再将虚拟地址空间和这个新的页面映射上.那么只需在下钩前先想办法消掉页面的写时复制属性,再hook ntdll的时候就能在Ring3下实现类似Ring0钩

PHP写时复制

原文:http://www.huamanshu.com/blog/2014-05-18.html 起源 写时复制英文名字叫“copy-on-write”,简称“cow”,又名“靠” 今天查了下这个"cow",原来起源于*nix内存管理机制,后来广泛应用在其它开发语言上,C++的STL,还有PHP,相信很多人都了解这个写时复制,经常跟别人侃得甚欢. $foo = 'foo'; $bar = $foo; 不会 初写这样的PHP代码时,常会问,这样的话会不会因为复制而占用更多内存,旁边会有人

linux 写时复制 copyonwrite

如果多个进程当父进程产生一个子进程时,会把父进程的代码段.数据段等拷贝给子进程,这里边有一个写时拷贝原则,这个动作并不是马上执行的,要等到子进程去修改内存里面的变量时候,才会进行拷贝,拷贝的机制并不是想象的那么简单,拷贝的时候只是拷贝子进程所需要的页,每个进程os都会进行分页管理,虚拟的内存空间2^32远远大于物理内存空间,然后把物理内存分成一页一页的形式,如果这个变量放在第一页,就只会进行第一页的拷贝,这个时候要操作第二页的数据时候,这个时候操作系统会进行一个软中断,也就是缺页中断,之后再把父

php变量之写时复制机制(copy on write)

编程思想虽然可以共用,不过语言间的差异还是比较明显的,只是使用者之间没有意识到而己,而了解其中的差异对于编写程序以及把握性能还是有好处的.下面我们来介绍下PHP的一个很重要的机制copy on write,我们先以最简单的变量来介绍这个机制,在说这个之前,笔者先来介绍下弱类型是怎么实现的. 大家都知道,PHP是由C实现的,可是C是强类型语言,PHP怎么做到弱类型语言.一起来看下,PHP变量在C语言低层中的代码, typedef struct _zval_struct zval; typedef

php底层--4 写时复制

变量的赋值与引用 例如:$a=3; $b=$a; 这个时候是否就产生了2个结构体呢? No,如果是的话,这两个结构体的type,value全都一样,很浪费呀,所以在PHP实现的时候并没有copy一个结构体出来,而是$a,$b共用一个结构体. 在传值赋值时,并没有新生结构体,而是共用的. $a=3; 产生一个结构体 zvalue:3; type:IS_LONG; refcount_gc:1; is_ref_gc:0; $b=$a; 这个时候并没有新产生一个结构体,而是原来的结构体 refcount

JAVA中写时复制(Copy-On-Write)Map实现

1,什么是写时复制(Copy-On-Write)容器? 写时复制是指:在并发访问的情景下,当需要修改JAVA中Containers的元素时,不直接修改该容器,而是先复制一份副本,在副本上进行修改.修改完成之后,将指向原来容器的引用指向新的容器(副本容器). 2,写时复制带来的影响 ①由于不会修改原始容器,只修改副本容器.因此,可以对原始容器进行并发地读.其次,实现了读操作与写操作的分离,读操作发生在原始容器上,写操作发生在副本容器上. ②数据一致性问题:读操作的线程可能不会立即读取到新修改的数据

c++ string写时复制

string写时复制:将字符串str1赋值给str2后,除非str1的内容已经被改变,否则str2和str1共享内存.当str1被修改之后,stl才为str2开辟内存空间,并初始化. #include <cstring> #include <string> #include <cstdio> #include <iostream> using namespace std; void fun1() { string s1 = "hello, worl