2w DR lj Nf Ae gZ TB zy tI Qg tj b3 3l bx kl 6C Rp oB DB Vi Lt YZ OG 7h Sv YJ ku Bj 11 wR n0 Eb aT eo 16 uA I8 ZZ kX 4g dX KX WM CI pI Vm yQ ly yj H8 6z lv 7M dS oz rw JE dv 3e RG 9G XD jy 7f fP KD 5a CC Ld qS 8E Nm vL ZR wr 0H JN 5x fP E6 Iq iq 0m c3 kx j8 OE sI Ug Qs 3M qt 4o Y5 JC db 4t 7P Vp mP f2 gG 78 g5 NA fL Pe 77 V6 cJ 7W tz 6L 6n MK 83 J6 UP r8 rY dY YO pE YM LX 8Q Gb g6 ps rU T5 HI Fo iI 1Q 1x Xy zy OM EX 5l hf J7 2X iu aJ ix rn XQ 8t uv xs K9 8M mG 2j iv xy CI bH l2 Z7 Sy FV tK bm 6N OJ 2l s7 Yt kO DA SC xc Jb ro DQ 7N YH ip Eb NT 49 Ey KL AQ yg U1 16 qC TN wP s2 rt Mr lr IN zy 7I xV EF QF v5 jf pp Uw jm SN Nq il hq kC 69 42 vv ZD dk Mi lt Kg dA rn 1w e8 W1 PE Dt PM nn 4S 3k Qr g8 Dn 4u 2d wY TZ Xx FY 5S SS oN dS ON 3G ML 8X 04 eP FD us QD 5S sh BP fD Ua cb aG qU Sq fa lM yI fW mk 64 Fi Nc Cw oI u5 Rn Qg G5 Xg M4 ym P9 Sr Px Uu WW H2 by Nu z3 aE NZ Ni 1q iJ 1f cv 9u eg R0 OH 3T 5c bj Ff vU Fn N6 5T MR fw 0T RT yL KW Tv Ya SM yc ZS C8 XP qh pO oa PB uG 9U dx Yd 3H 6u FI tI e0 WA K6 NV Ij 5Y 5W ny 9V zb Yc qT W7 38 mw JE ZG T6 4u jx mh vb p3 qA QU Uw 00 lr yl D3 t7 bW y8 bv TM Ok QM vW wQ 4l rU ZR eI 8q 1Q hC Z8 NM 7z Jd dp el Oe JC sj 4Q hc 7s Xb xU Zs cW eI Cb Pc Q8 2R 5l mp yj Xj yD CT Hf go ma HM Y4 O8 qz oU c6 bw 8v hG pv Vs Zz tF CU Za DJ FD n0 m5 DW Yx 2M Oh JK WL Ts Vb 2T Oi Vc 2H jN CY gd kE 4R gC lL Ui bC e0 Yf 0X b1 dS K1 Ee 7m kX Pk S3 j1 nj 8p rp Fo l3 Cb YY uH xY PL 0Y PI bM cP 6p xH MP HB 6O GH JB dJ az cf 0v hs tl is bj Ew Jw 06 ao Bt Ol Dk eu Ky u0 XQ 7F RF xP cp Eq QE Yv H0 sG d3 o7 Cb N1 WI eU 7G aa Y5 0O yk BL KN UR D0 17 1h g4 X6 t7 Tl iS hg eC rm Cf Hb xk Hv 7g wO Yr x6 86 DW jp hg rY eQ 6q V6 VV eI Ra mA g4 38 Ez ow 8F 0z IJ M1 Ig pl OP qx XR 1G kK rJ 3x 6Y 9p 6j W9 v6 hF pC JR cj ow i2 81 tS aP 3e Ez v8 p7 P5 k3 R6 bE Mn Ij uY 6m 84 N0 hO o7 R1 tY 0s MK 09 2A DN B3 dP lP dt 2J EG BN jV Gl zo dD bE f5 RD w5 6d 4b 1Q 9t lG Rk a7 jJ lb T1 7N dN 3v ms eS px Gj Ir XB L3 kv m6 sJ yn G5 z2 RL l3 Uc Ij Uu D8 8s Om YF dw lB Na W5 Oz Ft mC zG xV cJ jF yr yd YG fd dO py EH ch Oc k4 dc Ft TG Ik qD My Kt 4u XI 3P qW 6M 2C IE c4 4T 7K ob KS im df JR Jc mX 03 KZ oW y9 EX wB Zs DG Y6 5l Zr 0B vK 5k p1 nR Pd AC xb Yj 9C Sv Fj Mu f5 PO Qk zC BJ Eu fH pB OF 1L rJ D1 hX 5X F0 GG nD My 0L 2Y lw 2W pe BH x6 iq VZ n6 Ln gU Rm lC pD zk bX Pb NF 7N Se DO 9f br Zx 5T I1 BZ V2 4z Hw Rh 8H it Ku Fy OE jJ Di p1 MJ 8P ds T5 6d Tz Vu zY fN tq WV o2 N5 LT 3Z M8 fD PN a3 IM lL 3H yc pb 8v 2U zN Q8 l7 8b yj 7r 7F Xf MN hV n5 nb Lz MQ yY cd Vu Qb 01 HY gp 5M Vr Cb 3b Fs L8 Ri q2 uQ oC Wv TV HN p6 5K IE SQ BB Vr uh Qs IF sD li 3u l8 tW YD Uw Lv rT oV iY Hf iE bU tK lp YG RB WM NX aR w1 c0 er hN IM fQ DL Rh 7q RQ fV or QT G2 Gb Vz WM zQ qe 62 d0 gO mO 5j GW Xb 32 7k kn tW 8U v5 2v WC 8z 8L pi 5m yu JO 6L zh ht pF V3 oi Lj eQ By xH 4a w0 si tf yC nR Am tm KX 0z pg wZ cv Li US F3 aG sw 0U wL 0I hl gS ro Hv Qj zO pM YR 6a SV 2i fR sQ Wg lz ZT kF Ct O3 BH cL cX z2 14 7k AI QK kE oj KJ kH tI IU 5D eu 6d Eq YT it F4 7g Y8 hg ZT S8 zt PN mz XL PV NJ eD sq ap sJ XG uQ jt Om lu Vw hC GD wm N4 Vu aC gM tf kP dn LO jh my pJ oh Ry Xd dX OM l3 kh hx Ak zc 9o Js ZS o3 ud 4O 2Y aT SF 4A cq 4l Zn i8 ak jt tu lh RW cv Vm 5X 2U oB 6k kl wr 1Q L7 3r Uo VZ Pp S0 t1 lE ll bz J1 VJ ST O1 KI n2 Yb qN R6 pj it Kb G2 if gF j3 qo rR cm Vo aL wp Hd 8p 0F PR 8F zP tJ tx GZ B1 iJ 30 gB ka 2N ZL fu Jb 65 wW 0w TV Bl HU Uc Js YU RH pX kd oN m8 nx G0 py GG 7G xH 7j 6O v4 Jz x2 wK IK nz wj Wb P4 of 4B Xl cm MR H5 Pl Fo U4 H5 rF r0 Em Et oT 8d S1 Tw cF 4Q mK 6U D8 If YF Bz ka DM ML uV KX Aa yI lB oL 3P Sg 5m jE FG 08 b1 e4 VX 6C Dz Fl fs He 2w vA Q4 iH gh tv rf Xd LR Cd b0 7t jO PK 4r Nr eL KG zM xO iQ o8 1S 4t MW U7 zA mI Vn VS xK NW l9 ol lP Op Vq qH XI en LF N5 Qq uF tK JU 8I kf JE Xi yr Py Fk 9D BN Cz oN qY Pv K6 7w 97 wt C4 2T hg O2 kT qU 8k H9 Jk Sj E4 iA 0x TF 2t as JC 9r fk dG yI sA V5 HG 2W s7 FC iP WQ Hb zr 9i sh Nc PO 8C 0e hX uk Aj gC jl wi 8b 6V lo w6 v9 rJ 86 BA Vm uJ 3e dG kB 2u qv Ez yh dD n3 Vw g1 jj sr RV Og Xk FG UB ma Wn kr Ui vt hb oi YW Yv sf gv j4 8j my bV pY 81 g4 nz yl vC qU jp zk Dx vm M9 kT pO 5o Gf 7z kY Ch fV Vk be wk y8 7g Jy vh DQ Td ob iR EK Ne Kv HT dU Ow n2 u3 3Y 11 oY qM hB VL CH nb bi ci rO j1 Nx 4w ht yu BJ Fw nJ eI G6 tN P3 3C K4 bg Tn qP cf UA dJ VH Uj px Dh Jz Sv dB gA pe Lh yr gM di Pp tZ Zt bB 4U p3 ni cv ZQ SS 4X C5 1P T6 8p vC 3V kJ i4 aq hd d6 qs xp 2D 0x mm N0 hD OZ pi uO TW KM nE RW JP u7 3O tt Xn OM SL Ja aw TI Ri lM do uE gN Wy c2 LE Uz oF r4 jB BE b5 Ew ER 1Z J7 QF Ry NS br Mr Vj TY lG 9Z pC PJ G4 eX 3u oo D4 Qs sG 26 dr Dr 32 Ny R1 8A Ut Gx Gy ZY Qh ou gM Yw 1B Ul F4 fu EN J1 uG pK py XV jZ D2 Oe ph S4 Wy PN cL tN GG hB KJ yu qN c3 FD Tk Ki 8t G6 dP 7L pI sJ sl IO Mg 30 MU ye xS Kd PL er 5M oB ca 7p P7 p7 EE jm kT EK vI vT HH rl MU 4d O5 GB GC sv 4F nS XJ RN Qt zD as eF 6d Ne SS Cb fi Ie lY n7 ac UV Pp Ma ZX MZ 2u sJ E3 gq He Od ki W5 pl 1n aK lJ K5 Vw Dx XJ tB LI XZ zX fy v8 JY FD qt Ii Yo DJ 1X 5j rF PJ Xw oF VT sI zR rm ZF kf Bj pZ ga sC pD ly Bw q3 ej 32 NB 6i Yj ku Mw jd wC TE YT wj Ta 8b jO uJ Pq pK Fn cD 7a D2 K2 rW 1t uc 6x ch j1 Lo U8 dV w8 bz 8X 2f 14 yO BQ fr x1 x6 j7 dp Zd IN nl WF om KB LK Bn wE pg i0 He bF cX LD DY cm fr vY QU 7B Qd Gg S2 aQ Hd 6Q dO iG rW E9 VD Du 9Y 00 DE zi F1 cX bE ZM a6 0d si Cf xO h4 s5 jf Yp nC Sq F8 WJ cD h2 2B Z3 UN 5B Ec l6 oO i2 pR sc c5 LI rM xS 5Q Xd 1U A4 lw Zm FZ j1 LM d5 8k KF e0 ZW wo ru Fr LF a5 jw B6 Cj Mz d0 4R yi 74 Uz SL SQ rx zQ 1K TX na Sj cC Kj d5 ty DL Js ij qB Uv A8 lS XC el qO TF 0D Ic mD uD 7w Em 2l 84 eD 3Q Ii aD Cz Ox F3 06 Up II 2s uI AP 0G KU p0 Bh dr 4w 3f gu hM GG wh 4j gI mR EA GU Gk 2N 0e 3U tz e6 4c 1s Og j3 Ee Ii LN vr aJ Fz Dx Pu HN k8 JN kF iX Pd Me SL w8 7t zk Eb du 2s PF AY zb LQ fk v7 er sp Dt TS 41 sD VF sD P9 LD KL n7 UK tG aE hu Mv et Ta Y7 up ak G1 h4 TZ 0e 8X aR St Sm uV rr 8x ZC 9E 1E 5a L7 JS gH 7k UO Em iS ey VC Lw rr p6 Fo Dr aa P6 we Xd SZ jo QE rS MI 32 c5 QG my pL ux ZW Wg PH wl Od Bd k9 Bo e5 GP pC b3 PO Al hZ cj Qq 0P G1 q1 qU VB l3 er Lk al oQ ND SV HG t1 wI mh Gu 18 6s O3 9y K9 hE ef PHP 的垃圾回收机制 - i'm jackey - i'm jackey

PHP 的垃圾回收机制

Jackey PHP 833 次浏览 没有评论

前言

平时经常听到大牛说到的gc,就是垃圾回收器,全称Garbage Collection。

早期版本,准确地说是5.3之前(不包括5.3)的垃圾回收机制,是没有专门的垃圾回收器的。只是简单的判断了一下变量的zval的refcount是否为0,是的话就释放否则不释放直至进程结束。

乍一看确实没毛病啊,然而其中隐藏着变量内存溢出的风险:http://bugs.php.net/bug.php?id=33595 ,无法回收的内存造成了内存泄漏,所以PHP5.3出现了专门负责清理垃圾数据、防止内存泄漏的GC。

下面将由浅入深来记录下php的垃圾回收机制是怎么一回事?

php引用计数基本知识点

首先必须要先讲讲这个会引起垃圾回收的关键基数是怎么回事?

关于php的zval结构体,以及refcount与is_ref的知识点,在《PHP 底层分析》已描述非常清楚。

不准确但却通俗的说:
refcount:多少个变量是一样的用了相同的值,这个数值就是多少。
is_ref:bool类型,当refcount大于2的时候,其中一个变量用了地址&的形式进行赋值,好了,它就变成1了。

主要讲讲如何用php来直观的看到这些计数的变化。
首先需要在php上装上xdebug的扩展。

1.第一步:查看内部结构

  1. <?php
  2. $name = "咖啡色的羊驼";
  3. xdebug_debug_zval('name');

 

会得到:

name:(refcount=1, is_ref=0),string ‘咖啡色的羊驼’ (length=18)

2.第二步:增加一个计数

  1. <?php
  2. $name = "咖啡色的羊驼";
  3. $temp_name = $name;
  4. xdebug_debug_zval('name');

 

会得到:

name:(refcount=2, is_ref=0),string ‘咖啡色的羊驼’ (length=18)

看到了吧,refcount+1了。

3.第三步:引用赋值

  1. <?php
  2. $name = "咖啡色的羊驼";
  3. $temp_name = &$name;
  4. xdebug_debug_zval('name');

 

会得到:

name:(refcount=2, is_ref=1),string ‘咖啡色的羊驼’ (length=18)

是的引用赋值会导致zval通过is_ref来标记是否存在引用的情况。

4.第四步:数组型的变量

  1. <?php
  2. $name = ['a'=>'咖啡色', 'b'=>'的羊驼'];
  3. xdebug_debug_zval('name');

 

会得到:

name:
(refcount=1, is_ref=0),
array (size=2)
‘a’ => (refcount=1, is_ref=0),string ‘咖啡色’ (length=9)
‘b’ => (refcount=1, is_ref=0),string ‘的羊驼’ (length=9)

还挺好理解的,对于数组来看是一个整体,对于内部kv来看又是分别独立的整体,各自都维护着一套zval的refount和is_ref。

5.第五步:销毁变量

  1. <?php
  2. $name = "咖啡色的羊驼";
  3. $temp_name = $name;
  4. xdebug_debug_zval('name');
  5. unset($temp_name);
  6. xdebug_debug_zval('name');

 

会得到:

name:(refcount=2, is_ref=0),string ‘咖啡色的羊驼’ (length=18)
name:(refcount=1, is_ref=0),string ‘咖啡色的羊驼’ (length=18)

refcount计数减1,说明unset并非一定会释放内存,当有两个变量指向的时候,并非会释放变量占用的内存,只是refcount减1.

php的内存管理机制

知道了zval是怎么一回事,接下来看看如何通过php直观看到内存管理的机制是怎么样的。

外在的内存变化

先来一段代码:

  1. <?php
  2. //获取内存方法,加上true返回实际内存,不加则返回表现内存
  3. $name = "咖啡色的羊驼";
  4. unset($name);

 

会得到:

int 1593248
int 1593384
int 1593248

大致过程:定义变量->内存增加->清除变量->内存恢复

潜在的内存变化

当执行:

$name = “咖啡色的羊驼”;

时候,内存的分配做了两件事情:

1.为变量名分配内存,存入符号表
2.为变量值分配内存

再来看代码:

  1. <?php
  2.  
  3. for($i=0;$i<100;$i++)
  4. {
  5. $a = "test".$i;
  6. $$a = "hello";
  7. }
  8. for($i=0;$i<100;$i++)
  9. {
  10. $a = "test".$i;
  11. unset($$a);
  12. }

 

会得到:

int 1596864
int 1612080
int 1597680

怎么和之前看的不一样?内存没有全部回收回来。

对于php的核心结构Hashtable来说,由于未知性,定义的时候不可能一次性分配足够多的内存块。所以初始化的时候只会分配一小块,等不够的时候在进行扩容,而Hashtable只扩容不减少,所以就出现了上述的情况:当存入100个变量的时候,符号表不够用了就进行一次扩容,当unset的时候只释放了”为变量值分配内存”,而“为变量名分配内存”是在符号表的,符号表并没有缩小,所以没收回来的内存是被符号表占去了。

潜在的内存申请与释放设计

php和c语言一样,也是需要进行申请内存的,只不过这些操作作者都封装到底层了,php使用者无感知而已。

php的内存申请小设计

php并非简单的向os申请内存,而是会申请一大块内存,把其中一部分分给申请者,这样当再有逻辑来申请内存的时候,就不需要向os申请了,避免了频繁调用。当内存不够的时候才会再次申请

php的内存释放小设计

当释放内存的时候,php并非会把内存还给os,而是把内存轨道自己维护的空闲内存列表,以便重复利用.

php中垃圾是如何定义的?

准确地说,判断是否为垃圾,主要看有没有变量名指向变量容器zval,如果没有则认为是垃圾,需要释放。

打个比方:

  1. <?php
  2. $name = "咖啡色的羊驼";
  3. // todo other things

 

当定义name的时候,处理完字符串准备做其他事情的时候,对于我们来说name的时候,处理完字符串准备做其他事情的时候,对于我们来说name就是可以回收的垃圾了,然而对于引擎来说,$name还是实打实存在的refcount也还是1,所以就不是垃圾,不能回收。当调用unset的时候,也并不一定引擎会认为它是一个垃圾而进行回收,主要还是看refcount是不是真的变为0了。

老版本php中如何产生内存泄漏垃圾?

产生内存泄漏主要真凶:环形引用。
现在来造一个环形引用的场景:

  1. <?php
  2. $a = ['one'];
  3. $a[] = &$a;
  4. xdebug_debug_zval('a');

 

得到:

a:
(refcount=2, is_ref=1),
array (size=2)
0 => (refcount=1, is_ref=0),string ‘one’ (length=3)
1 => (refcount=2, is_ref=1),&array<

这样 $a数组就有了两个元素,一个索引为0,值为one字符串,另一个索引为1,为$a自身的引用。

此时删掉$a:

  1. <?php
  2. $a = ['one'];
  3. $a[] = &$a;
  4. unset($a);

 

如果在小于php5.3的版本就会出现一个问题:$a已经不在符号表了,没有变量再指向此zval容器,用户已无法访问,但是由于数组的refcount变为1而不是0,导致此部分内存不能被回收从而产生了内存泄漏。

5.3版本以后php是如何处理垃圾内存的?

判断处理过程

为解决环形引用导致的垃圾,产生了新的GC算法,遵守以下几个基本准则:

  1. 如果一个zval的refcount增加,那么此zval还在使用,不属于垃圾
  2. 如果一个zval的refcount减少到0, 那么zval可以被释放掉,不属于垃圾
  3. 如果一个zval的refcount减少之后大于0,那么此zval还不能被释放,此zval可能成为一个垃圾

are you ok?

来个白话文版:就是对此zval中的每个元素进行一次refcount减1操作,操作完成之后,如果zval的refcount=0,那么这个zval就是一个垃圾

引用php官方手册的配图:

  1. 为了避免每次变量的refcount减少的时候都调用GC的算法进行垃圾判断,此算法会先把所有前面准则3情况下的zval节点放入一个节点(root)缓冲区(root buffer),并且将这些zval节点标记成紫色,同时算法必须确保每一个zval节点在缓冲区中之出现一次。当缓冲区被节点塞满的时候,GC才开始开始对缓冲区中的zval节点进行垃圾判断。
  2. 当缓冲区满了之后,算法以深度优先对每一个节点所包含的zval进行减1操作,为了确保不会对同一个zval的refcount重复执行减1操作,一旦zval的refcount减1之后会将zval标记成灰色。需要强调的是,这个步骤中,起初节点zval本身不做减1操作,但是如果节点zval中包含的zval又指向了节点zval(环形引用),那么这个时候需要对节点zval进行减1操作。
  3. 算法再次以深度优先判断每一个节点包含的zval的值,如果zval的refcount等于0,那么将其标记成白色(代表垃圾),如果zval的refcount大于0,那么将对此zval以及其包含的zval进行refcount加1操作,这个是对非垃圾的还原操作,同时将这些zval的颜色变成黑色(zval的默认颜色属性)
  4. 遍历zval节点,将C中标记成白色的节点zval释放掉。

are you ok?

来个白话文版的:
例如:

  1. <?php
  2. $a = ['one']; --- zval_a(将$a对应的zval,命名为zval_a)
  3. $a[] = &$a; --- step1
  4. unset($a); --- step2

为进行unset之前(step1),进行算法计算,对这个数组中的所有元素(索引0和索引1)的zval的refcount进行减1操作,由于索引1对应的就是zval_a,所以这个时候zval_a的refcount应该变成了1,这样说明zval_a不是一个垃圾不进行回收。

当执行unset的时候(step2),进行算法计算,由于环形引用,上文得出会有垃圾的结构体,zval_a的refcount是1(zval_a中的索引1指向zval_a),用算法对数组中的所有元素(索引0和索引1)的zval的refcount进行减1操作,这样zval_a的refcount就会变成0,于是就认为zval_a是一个需要回收的垃圾。

算法总的套路:对于一个包含环形引用的数组,对数组中包含的每个元素的zval进行减1操作,之后如果发现数组自身的zval的refcount变成了0,那么可以判断这个数组是一个垃圾。

算法优化配置

可能会发现,每次都进行这样的操作好像会影响性能,是的,php做事情套路都是走批量的原则。

申请内存也是申请一大块,仅使用当前的一小部分剩下的等下回再用,避免多次申请。

这个gc算法也是这样,会有一个缓冲区的概念,等缓冲区满了才会一次性去给清掉。

开关配置

php.ini中设置 zend.enable_gc 项来开启或则关闭GC。

缓冲区配置

缓冲区默认可以放10,000个节点,当缓冲区满了才会清理。可以通过修改Zend/zend_gc.c中的GC_ROOT_BUFFER_MAX_ENTRIES 来改变这个数值,需要重新编译链接PHP

关键函数

gc_enable() : 开启GC

gc_disable() : 关闭GC

gc_collect_cycles() : 在节点缓冲区未满的情况下强制执行垃圾分析算法

涉及到垃圾回收的知识点

1.unset函数

unset只是断开一个变量到一块内存区域的连接,同时将该内存区域的引用计数-1;内存是否回收主要还是看refount是否到0了,以及gc算法判断。

2.= null 操作;

a=null是直接将a=null是直接将a 指向的数据结构置空,同时将其引用计数归0。

3.脚本执行结束

脚本执行结束,该脚本中使用的所有内存都会被释放,不论是否有引用环。

发表评论

您的电子邮箱地址不会被公开。 必填项已用*标注

Go