php的GC機制

在php5.3版本之前, php變量的回收機制只是簡單的通過計數來處理(當refcount=0時,會回收內存),但這樣會出現一個問題

$a=array("str");

$a[]=&$a;

unset($a);

執行unset之前,$a的refcount 為2,執行unset之后,$a的refcout為1,因為是1不等于0,不能被回收內存,即為垃圾,當然,在php腳本執行完畢后,所分配的內存將全部被回收,但是現在php除了應用于腳本以外,更多的地方用于寫守護服務(當然我不推薦),可能長達一個月,兩個月才結束腳本,這期間例如上面的程序會產生內存溢出

?注:unset并不能釋放內存,需要看zval的refcount是否為0

php5.3以后增了GC垃圾回收機制

在分配zval時,以zval_gc_info為單位

?

#define ALLOC_ZVAL(z)                                     \do {                                                \(z) = (zval*)emalloc(sizeof(zval_gc_info));        \GC_ZVAL_INIT(z);                                \} while (0)typedef struct _zval_gc_info {zval z;union {gc_root_buffer       *buffered;struct _zval_gc_info *next;} u;
} zval_gc_info;#define FREE_ZVAL(z)                                     \do {                                                \GC_REMOVE_ZVAL_FROM_BUFFER(z);                    \efree(z);                                        \} while (0)

?

?

typedef struct _gc_root_buffer {struct _gc_root_buffer   *prev;        /* double-linked list               */struct _gc_root_buffer   *next;zend_object_handle        handle;    /* must be 0 for zval               */union {zval                 *pz;zend_object_handlers *handlers;} u;
} gc_root_buffer;

?

?

?

php的GC回收機制啟動時,會分配10000個gc_root_buffer的空間

ZEND_API void gc_init(TSRMLS_D)  
{  if (GC_G(buf) == NULL && GC_G(gc_enabled)) {  GC_G(buf) = (gc_root_buffer*) malloc(sizeof(gc_root_buffer) * GC_ROOT_BUFFER_MAX_ENTRIES);  GC_G(last_unused) = &GC_G(buf)[GC_ROOT_BUFFER_MAX_ENTRIES];  gc_reset(TSRMLS_C);  }  
}  

?

在unset($a)時,詳見這里 在active_systom_table找到key為a的zval后,對其value執行析構函數,將其zval的refcount-1,若減1后的值為0,說明可能直接釋放內存,若為大于0,放到gc_root_buffer中

ZEND_API void _zval_ptr_dtor(zval **zval_ptr ZEND_FILE_LINE_DC) /* {{{ */  
{   Z_DELREF_PP(zval_ptr);  if (Z_REFCOUNT_PP(zval_ptr) == 0) {  TSRMLS_FETCH();  if (*zval_ptr != &EG(uninitialized_zval)) {  GC_REMOVE_ZVAL_FROM_BUFFER(*zval_ptr);  zval_dtor(*zval_ptr);  efree_rel(*zval_ptr);  }  } else {  TSRMLS_FETCH();  if (Z_REFCOUNT_PP(zval_ptr) == 1) {  Z_UNSET_ISREF_PP(zval_ptr);  }  GC_ZVAL_CHECK_POSSIBLE_ROOT(*zval_ptr);  }  
}  

?

?

?

#define GC_ZVAL_CHECK_POSSIBLE_ROOT(z) /gc_zval_check_possible_root((z) TSRMLS_CC)static zend_always_inline void gc_zval_check_possible_root(zval *z TSRMLS_DC)
{if (z->type == IS_ARRAY || z->type == IS_OBJECT) {gc_zval_possible_root(z TSRMLS_CC);}
}ZEND_API void gc_zval_possible_root(zval *zv TSRMLS_DC)
{if (UNEXPECTED(GC_G(free_list) != NULL &&GC_ZVAL_ADDRESS(zv) != NULL &&GC_ZVAL_GET_COLOR(zv) == GC_BLACK) &&(GC_ZVAL_ADDRESS(zv) < GC_G(buf) ||GC_ZVAL_ADDRESS(zv) >= GC_G(last_unused))) {/* The given zval is a garbage that is going to be deleted by* currently running GC */return;}if (zv->type == IS_OBJECT) {GC_ZOBJ_CHECK_POSSIBLE_ROOT(zv);return;}GC_BENCH_INC(zval_possible_root);//如果zv中的gc_root_buffer最后兩位不是紫色,則進行處理if (GC_ZVAL_GET_COLOR(zv) != GC_PURPLE) {GC_ZVAL_SET_PURPLE(zv); //設置為紫色if (!GC_ZVAL_ADDRESS(zv)) {gc_root_buffer *newRoot = GC_G(unused);if (newRoot) {GC_G(unused) = newRoot->prev;} else if (GC_G(first_unused) != GC_G(last_unused)) {newRoot = GC_G(first_unused);GC_G(first_unused)++;} else {if (!GC_G(gc_enabled)) {GC_ZVAL_SET_BLACK(zv);return;}zv->refcount__gc++;gc_collect_cycles(TSRMLS_C);zv->refcount__gc--;newRoot = GC_G(unused);if (!newRoot) {return;}GC_ZVAL_SET_PURPLE(zv);GC_G(unused) = newRoot->prev;}newRoot->next = GC_G(roots).next;newRoot->prev = &GC_G(roots);GC_G(roots).next->prev = newRoot;GC_G(roots).next = newRoot;GC_ZVAL_SET_ADDRESS(zv, newRoot); //將gc_root_buffer放到zval_gc_info結構體中newRoot->handle = 0;newRoot->u.pz = zv;GC_BENCH_INC(zval_buffered);GC_BENCH_INC(root_buf_length);GC_BENCH_PEAK(root_buf_peak, root_buf_length);}}
}

?

?

將當前zval放到gc_root_bufer中,每個zval只放一次,依據是該zval所在的zval_gc_info中的gc_root_buffer的顏色 是否是 紫色

?

#define GC_ZVAL_GET_COLOR(v) \
GC_GET_COLOR(((zval_gc_info*)(v))->u.buffered)

#define GC_GET_COLOR(v) \
(((zend_uintptr_t)(v)) & GC_COLOR)

?

若不是紫色,則設置為紫色

#define GC_ZVAL_SET_PURPLE(v) \
GC_SET_PURPLE(((zval_gc_info*)(v))->u.buffered)

#define GC_SET_PURPLE(v) \
(v) = ((gc_root_buffer*)(((zend_uintptr_t)(v)) | GC_PURPLE))

?

宏GC_ZVAL_SET_ADDRESS(zv, newRoot);用來將gc_root_buffer newRoot 放到zv相應位置

#define GC_COLOR  0x03#define GC_BLACK  0x00
#define GC_WHITE  0x01
#define GC_GREY   0x02
#define GC_PURPLE 0x03#define GC_ZVAL_SET_ADDRESS(v, a) \GC_SET_ADDRESS(((zval_gc_info*)(v))->u.buffered, (a))#define GC_SET_ADDRESS(v, a) \(v) = ((gc_root_buffer*)((((zend_uintptr_t)(v)) & GC_COLOR) | ((zend_uintptr_t)(a))))

?

?

GC_ZVAL_SET_ADDRESS宏中先將v強制轉為zval_gc_info類型,本身在為zval分配內存時, 就是以zval_gc_info為單位的,將強轉為zval*,因為只需要對zval結構體填充數據,不需要 gc_root_buffer *bufer這個樣成員
因為在結構體zval_gc_val中,zval z是第一個成員,那么z的地址也是zval_gc_info本身的內存地址

在PHP GC中,使用顏色來標明垃圾的處理過程

指針無論在32位機或64位機,最后兩位均為0,

gc_collect_cycles處理垃圾

 


ZEND_API int gc_collect_cycles(TSRMLS_D)
{int count = 0;if (GC_G(roots).next != &GC_G(roots)) {zval_gc_info *p, *q, *orig_free_list, *orig_next_to_free;if (GC_G(gc_active)) {return 0;}GC_G(gc_runs)++;GC_G(zval_to_free) = FREE_LIST_END;GC_G(gc_active) = 1;gc_mark_roots(TSRMLS_C);gc_scan_roots(TSRMLS_C);gc_collect_roots(TSRMLS_C);orig_free_list = GC_G(free_list);orig_next_to_free = GC_G(next_to_free);p = GC_G(free_list) = GC_G(zval_to_free);GC_G(zval_to_free) = NULL;GC_G(gc_active) = 0;/* First call destructors */while (p != FREE_LIST_END) {if (Z_TYPE(p->z) == IS_OBJECT) {if (EG(objects_store).object_buckets &&EG(objects_store).object_buckets[Z_OBJ_HANDLE(p->z)].valid &&EG(objects_store).object_buckets[Z_OBJ_HANDLE(p->z)].bucket.obj.refcount <= 0 &&EG(objects_store).object_buckets[Z_OBJ_HANDLE(p->z)].bucket.obj.dtor &&!EG(objects_store).object_buckets[Z_OBJ_HANDLE(p->z)].destructor_called) {EG(objects_store).object_buckets[Z_OBJ_HANDLE(p->z)].destructor_called = 1;EG(objects_store).object_buckets[Z_OBJ_HANDLE(p->z)].bucket.obj.refcount++;EG(objects_store).object_buckets[Z_OBJ_HANDLE(p->z)].bucket.obj.dtor(EG(objects_store).object_buckets[Z_OBJ_HANDLE(p->z)].bucket.obj.object, Z_OBJ_HANDLE(p->z) TSRMLS_CC);EG(objects_store).object_buckets[Z_OBJ_HANDLE(p->z)].bucket.obj.refcount--;}}count++;p = p->u.next;}/* Destroy zvals */p = GC_G(free_list);while (p != FREE_LIST_END) {GC_G(next_to_free) = p->u.next;if (Z_TYPE(p->z) == IS_OBJECT) {if (EG(objects_store).object_buckets &&EG(objects_store).object_buckets[Z_OBJ_HANDLE(p->z)].valid &&EG(objects_store).object_buckets[Z_OBJ_HANDLE(p->z)].bucket.obj.refcount <= 0) {EG(objects_store).object_buckets[Z_OBJ_HANDLE(p->z)].bucket.obj.refcount = 1;Z_TYPE(p->z) = IS_NULL;zend_objects_store_del_ref_by_handle_ex(Z_OBJ_HANDLE(p->z), Z_OBJ_HT(p->z) TSRMLS_CC);}} else if (Z_TYPE(p->z) == IS_ARRAY) {Z_TYPE(p->z) = IS_NULL;zend_hash_destroy(Z_ARRVAL(p->z));FREE_HASHTABLE(Z_ARRVAL(p->z));} else {zval_dtor(&p->z);Z_TYPE(p->z) = IS_NULL;}p = GC_G(next_to_free);}/* Free zvals */p = GC_G(free_list);while (p != FREE_LIST_END) {q = p->u.next;FREE_ZVAL_EX(&p->z);p = q;}GC_G(collected) += count;GC_G(free_list) = orig_free_list;GC_G(next_to_free) = orig_next_to_free;}return count;
}

?

?
gc_mark_roots(TSRMLS_C); 為垃圾打下顏色標記,遍歷gc_root_buffer,其中的u.pz將紫色變更為灰色,遍歷u.pz該array zval的每個元素,將其refcount-1,
gc_collect_roots遍歷gc_root_buffer,如果refcount==0再設置為白色,表示為垃圾,若refcount >0,表示別人在使用,設置為黑色
 
static void gc_mark_roots(TSRMLS_D)
{gc_root_buffer *current = GC_G(roots).next;while (current != &GC_G(roots)) {if (current->handle) {if (EG(objects_store).object_buckets) {//處理對象,暫時不用看}} else {if (GC_ZVAL_GET_COLOR(current->u.pz) == GC_PURPLE) {zval_mark_grey(current->u.pz TSRMLS_CC);} else {GC_ZVAL_SET_ADDRESS(current->u.pz, NULL);GC_REMOVE_FROM_BUFFER(current);}}current = current->next;}
}
 

?

?
static void zval_mark_grey(zval *pz TSRMLS_DC)
{Bucket *p;tail_call:if (GC_ZVAL_GET_COLOR(pz) != GC_GREY) {p = NULL;GC_BENCH_INC(zval_marked_grey);GC_ZVAL_SET_COLOR(pz, GC_GREY);if (Z_TYPE_P(pz) == IS_OBJECT && EG(objects_store).object_buckets) {//對象的處理, 暫時不用管} else if (Z_TYPE_P(pz) == IS_ARRAY) {if (Z_ARRVAL_P(pz) == &EG(symbol_table)) {GC_ZVAL_SET_BLACK(pz);} else {p = Z_ARRVAL_P(pz)->pListHead;}}while (p != NULL) {pz = *(zval**)p->pData;if (Z_TYPE_P(pz) != IS_ARRAY || Z_ARRVAL_P(pz) != &EG(symbol_table)) {pz->refcount__gc--;}if (p->pListNext == NULL) {goto tail_call;} else {zval_mark_grey(pz TSRMLS_CC);}p = p->pListNext;}}
}

?

?第二次遍歷gc_root_buffer,如果zv中的顏色為灰色,且refcount=0,再置為白色,若refcount>0,置為黑色(不是垃圾)

static void gc_scan_roots(TSRMLS_D)
{gc_root_buffer *current = GC_G(roots).next;while (current != &GC_G(roots)) {if (current->handle) {zval z;INIT_PZVAL(&z);Z_OBJ_HANDLE(z) = current->handle;Z_OBJ_HT(z) = current->u.handlers;zobj_scan(&z TSRMLS_CC);} else {zval_scan(current->u.pz TSRMLS_CC);}current = current->next;}
}

?

?

static int zval_scan(zval *pz TSRMLS_DC)
{Bucket *p;tail_call:    if (GC_ZVAL_GET_COLOR(pz) == GC_GREY) {p = NULL;if (pz->refcount__gc > 0) {zval_scan_black(pz TSRMLS_CC);} else { GC_ZVAL_SET_COLOR(pz, GC_WHITE);if (Z_TYPE_P(pz) == IS_OBJECT && EG(objects_store).object_buckets) {//處理object} else if (Z_TYPE_P(pz) == IS_ARRAY) {if (Z_ARRVAL_P(pz) == &EG(symbol_table)) {GC_ZVAL_SET_BLACK(pz);} else {p = Z_ARRVAL_P(pz)->pListHead;}}}while (p != NULL) {if (p->pListNext == NULL) {pz = *(zval**)p->pData;goto tail_call;} else {zval_scan(*(zval**)p->pData TSRMLS_CC);}p = p->pListNext;}}return 0;
}

?

?

遍歷gc_root_buffer鏈表,將zv顏色為白色的數據放置單獨一個鏈表,全部回收

static void gc_collect_roots(TSRMLS_D)
{gc_root_buffer *current = GC_G(roots).next;while (current != &GC_G(roots)) {if (current->handle) {if (EG(objects_store).object_buckets) {struct _store_object *obj = &EG(objects_store).object_buckets[current->handle].bucket.obj;zval z;GC_SET_ADDRESS(obj->buffered, NULL);INIT_PZVAL(&z);Z_OBJ_HANDLE(z) = current->handle;Z_OBJ_HT(z) = current->u.handlers;zobj_collect_white(&z TSRMLS_CC);}} else {GC_ZVAL_SET_ADDRESS(current->u.pz, NULL);zval_collect_white(current->u.pz TSRMLS_CC);}GC_REMOVE_FROM_BUFFER(current);current = current->next;}
}

?

?

static void zval_collect_white(zval *pz TSRMLS_DC)
{Bucket *p;tail_call:if (((zval_gc_info*)(pz))->u.buffered == (gc_root_buffer*)GC_WHITE) {p = NULL;GC_ZVAL_SET_BLACK(pz);if (Z_TYPE_P(pz) == IS_OBJECT && EG(objects_store).object_buckets) {...} else {if (Z_TYPE_P(pz) == IS_ARRAY) {p = Z_ARRVAL_P(pz)->pListHead;}}/* restore refcount and put into list to free */pz->refcount__gc++;((zval_gc_info*)pz)->u.next = GC_G(zval_to_free);GC_G(zval_to_free) = (zval_gc_info*)pz;while (p != NULL) {pz = *(zval**)p->pData;if (Z_TYPE_P(pz) != IS_ARRAY || Z_ARRVAL_P(pz) != &EG(symbol_table)) {pz->refcount__gc++;}if (p->pListNext == NULL) {goto tail_call;} else {zval_collect_white(pz TSRMLS_CC);}p = p->pListNext;}}
}

?

?

?

轉載于:https://www.cnblogs.com/taek/p/5453584.html

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/257216.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/257216.shtml
英文地址,請注明出處:http://en.pswp.cn/news/257216.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

Spring 框架的JDBC模板技術

1. 概述 Spring 框架提供了很多持久層的模板類來簡化編程;Spring 框架提供的JDBC模板類: JdbcTemplate 類;Spring 框架提供的整合 Hibernate 框架的模板類: HibernateTemplate 類2. 環境搭建 2.1 創建數據庫表結構 CREATE TABLE t_account(id INT PRIMARY KEY AUTO_INCREMENT,…

BZOJ 1692: [Usaco2007 Dec]隊列變換( 貪心 )

數據 n < 30000 , 然后 O( n ) 的貪心也過了..... USACO 數據是有多弱啊 ( ps : BZOJ 1640 和此題一模一樣 , 雙倍經驗 ) --------------------------------------------------------------------------------------#include<cstdio>#include<cstring>#include…

數據說話,88000條數據繪制北京市地圖

偶獲得一批數據&#xff0c;本著好玩的態度繪制下來看看到底是什么鬼&#xff0c;繪制的結果如下&#xff1a; 呵呵&#xff0c;什么都不像。而且中間最重要的部分因數據量過大繪制的已經看不清楚了。于是乎&#xff0c;縮小繪制范圍&#xff0c;去除周圍沒有用的數據。重新繪制…

我的第一個python web開發框架(11)——工具函數包說明(二)

db_helper.py是數據庫操作包&#xff0c;主要有兩個函數&#xff0c;分別是read()數據庫讀操作函數和write()數據庫寫操作函數。這個包的代碼是從小戴同學分享的博文改造過來的。 1 #!/usr/bin/env python2 # codingutf-83 4 import psycopg25 from common import log_helper6 …

ASP.NET:在一般處理程序中通過 Session 保存驗證碼卻無法顯示圖片?

1 using System.Drawing;2 using System.Web;3 using System.Web.SessionState;4 5 /// <summary>6 /// CaptchaHandler 的摘要說明7 /// </summary>8 public class CaptchaHandler : IHttpHandler, IRequiresSessionState  //簡記&#xff1a;我需要Session9 { …

[LINK]用Python計算昨天、今天和明天的日期時間

用Python計算昨天、今天和明天的日期時間 轉載于:https://www.cnblogs.com/Athrun/p/5477651.html

Windows系統下oracle數據庫每天定時備份

第一步&#xff1a;建立備份腳本oraclebackup.bat 首先建立一個備份bat文件&#xff0c;在D盤下新建備份目錄oraclebackup&#xff0c;將oracle安裝目錄下的EXP.EXE復制到此目錄下&#xff0c;再新建一個文本文件oraclebackup.txt&#xff0c;內容如下&#xff1a; echo off ec…

面試題3:二維數組查找

1 bool Find(const int *matrix, int rows, int columns, int number)2 {3 int key;4 int indexRow;5 int indexCol;6 7 /*合法性檢查*/8 if((NULL matrix)||(rows < 0)||(columns <0))9 { 10 return false; 11 } 12 13 /*提升…

linux crontab 命令

#method 1 crontab -e crontab -u root -e #不同用戶自己的任務計劃 crontab -l#method 2 vim /etc/crontab# Example of job definition: # .---------------- minute (0 - 59) # | .------------- hour (0 - 23) # | | .---------- day of month (1 - 31) # | | | .--…

[譯] RNN 循環神經網絡系列 2:文本分類

原文地址&#xff1a;RECURRENT NEURAL NETWORKS (RNN) – PART 2: TEXT CLASSIFICATION原文作者&#xff1a;GokuMohandas譯文出自&#xff1a;掘金翻譯計劃本文永久鏈接&#xff1a;github.com/xitu/gold-m…譯者&#xff1a;Changkun Ou校對者&#xff1a;yanqiangmiffy, To…

[置頂] Android開發者官方網站文檔 - 國內踏得網鏡像

Mark 一下&#xff1a; 鏡像地址&#xff1a;http://wear.techbrood.com/index.html Android DevelopTools: http://www.androiddevtools.cn/ 轉載于:https://www.cnblogs.com/superle/p/4561856.html

Java實現選擇排序

選擇排序思想就是選出最小或最大的數與第一個數交換&#xff0c;然后在剩下的數列中重復完成該動作。 package Sort;import java.util.Arrays;public class SelectionSort {public static int selectMinKey(int[] list, int beginIdx) {int idx beginIdx;int temp list[begin…

ASP.NET MVC中ViewData、ViewBag和TempData

1.ViewData 1.1 ViewData繼承了IDictionary<string, object>,因此在設置ViewData屬性時,傳入key必須要字符串型別,value可以是任意類型。 1.2 ViewData它只會存在這次的HTTP要求而已,而不像Session可以將數據帶到下HTTP要求。 public class TestController : Controller{…

java 正則表達式驗證郵箱格式是否合規 以及 正則表達式元字符

package com.ykmimi.testtest; /*** 測試郵箱地址是否合規* author ukyor**/ public class EmailTest {public static void main(String[] args) {//定義要匹配的Email地址的正則表達式//其中\w代表可用作標識符的字符,不包括$. \w表示多個// \\.\\w表示點.后面有\w 括號{2,3}…

鏡頭選型

景深&#xff1a; 光圈越大&#xff0c;光圈值越小&#xff0c;景深越小 光圈越小&#xff0c;光圈值越大&#xff0c;景深越深 焦距越長&#xff0c;視角越小&#xff0c;主體像越大&#xff0c;景深越小 主體越近&#xff0c;景深越小

迅雷賬號

賬號 jiangchnangli:1 密碼 892812 網址 http://www.s8song.net/read-htm-tid-4906661.html漫晴xydcq7681轉載于:https://www.cnblogs.com/wlzhang/p/4563118.html

【Swift學習】Swift編程之旅---ARC(二十)

Swift使用自動引用計數(ARC)來跟蹤并管理應用使用的內存。大部分情況下&#xff0c;這意味著在Swift語言中&#xff0c;內存管理"仍然工作"&#xff0c;不需要自己去考慮內存管理的事情。當實例不再被使用時&#xff0c;ARC會自動釋放這些類的實例所占用的內存。然而…

像元大小及精度

說完了光學系統的分辨率之后我們來看看相機的圖像分辨率。圖像分辨率比較好理解&#xff0c;就是單位距離內的像用多少個像素來顯示。以我們的ORCA-Flash4.0為例&#xff0c;芯片的像元大小為 6.5 μm&#xff0c;在 40X物鏡的放大倍率下&#xff0c;1 μm的物經光學系統放大為…

轉:傳入的表格格式數據流(TDS)遠程過程調用(RPC)協議流不正確 .

近期在做淘寶客的項目&#xff0c;大家都知道&#xff0c;淘寶的商品詳細描述字符長度很大&#xff0c;所以就導致了今天出現了一個問題 VS的報錯是這樣子的 ” 傳入的表格格式數據流(TDS)遠程過程調用(RPC)協議流不正確“ 還說某個desricption 過長之類的話 直覺告訴我&#…

合并bin文件-----帶boot發布版本比較好用的bat(便捷版)

直接上圖上代碼&#xff08;代碼在結尾&#xff09;&#xff0c;有不會用的可以留言&#xff1a; 第一步&#xff1a;工程介紹&#xff0c;關鍵點--- 1.bat文件放所在app和boot工程的同級目錄下 2.release為運行bat自動生成文件夾 第二步&#xff1a;合版.bat 針對具體項目需…