PHP5和7中的zval全介紹,記憶體管理,型別,引用計數(1)
轉自:http://0x1.im/blog/php/Internal-value-representation-in-PHP-7-part-1.html
本文第一部分和第二均翻譯自Nikita Popov(nikic,PHP 官方開發組成員,柏林科技大學的學生) 的部落格。為了更符合漢語的閱讀習慣,文中並不會逐字逐句的翻譯。
要理解本文,你應該對 PHP5 中變數的實現有了一些瞭解,本文重點在於解釋 PHP7 中 zval 的變化。
由於大量的細節描述,本文將會分成兩個部分:第一部分主要描述 zval(zend value) 的實現在 PHP5 和 PHP7 中有何不同以及引用的實現。第二部分將會分析單獨型別(strings、objects)的細節。
PHP5 中的 zval
PHP5 中 zval 結構體定義如下:
typedef struct _zval_struct {
zvalue_value value;
zend_uint refcount__gc;
zend_uchar type;
zend_uchar is_ref__gc;
} zval;
如上,zval 包含一個 value
、一個 type
以及兩個 __gc
字尾的欄位。value
是個聯合體,用於儲存不同型別的值:
typedef union _zvalue_value {
long lval ; // 用於 bool 型別、整型和資源型別
double dval; // 用於浮點型別
struct { // 用於字串
char *val;
int len;
} str;
HashTable *ht; // 用於陣列
zend_object_value obj; // 用於物件
zend_ast *ast; // 用於常量表達式(PHP5.6 才有)
} zvalue_value ;
C 語言聯合體的特徵是一次只有一個成員是有效的並且分配的記憶體與需要記憶體最多的成員匹配(也要考慮記憶體對齊)。所有成員都儲存在記憶體的同一個位置,根據需要儲存不同的值。當你需要 lval
的時候,它儲存的是有符號整形,需要 dval
時,會儲存雙精度浮點數。
需要指出的是是聯合體中當前儲存的資料型別會記錄到 type
欄位,用一個整型來標記:
#define IS_NULL 0 /* Doesn't use value */
#define IS_LONG 1 /* Uses lval */
#define IS_DOUBLE 2 /* Uses dval */
#define IS_BOOL 3 /* Uses lval with values 0 and 1 */
#define IS_ARRAY 4 /* Uses ht */
#define IS_OBJECT 5 /* Uses obj */
#define IS_STRING 6 /* Uses str */
#define IS_RESOURCE 7 /* Uses lval, which is the resource ID */
/* Special types used for late-binding of constants */
#define IS_CONSTANT 8
#define IS_CONSTANT_AST 9
PHP5 中的引用計數
在PHP5中,zval 的記憶體是單獨從堆(heap)中分配的(有少數例外情況),PHP 需要知道哪些 zval 是正在使用的,哪些是需要釋放的。所以這就需要用到引用計數:zval 中 refcount__gc
的值用於儲存
zval 本身被引用的次數,比如 $a
= $b = 42
語句中,42
被兩個變數引用,所以它的引用計數就是
2。如果引用計數變成 0,就意味著這個變數已經沒有用了,記憶體也就可以釋放了。
注意這裡提及到的引用計數指的不是 PHP 程式碼中的引用(使用 &
),而是變數的使用次數。後面兩者需要同時出現時會使用『PHP
引用』和『引用』來區分兩個概念,這裡先忽略掉 PHP 的部分。
一個和引用計數緊密相關的概念是『寫時複製』:對於多個引用來說,zaval 只有在沒有變化的情況下才是共享的,一旦其中一個引用改變 zval 的值,就需要複製(”separated”)一份 zval,然後修改複製後的 zval。
下面是一個關於『寫時複製』和 zval 的銷燬的例子:
<?php
$a = 42; // $a -> zval_1(type=IS_LONG, value=42, refcount=1)
$b = $a; // $a, $b -> zval_1(type=IS_LONG, value=42, refcount=2)
$c = $b; // $a, $b, $c -> zval_1(type=IS_LONG, value=42, refcount=3)
// 下面幾行是關於 zval 分離的
$a += 1; // $b, $c -> zval_1(type=IS_LONG, value=42, refcount=2)
// $a -> zval_2(type=IS_LONG, value=43, refcount=1)
unset($b); // $c -> zval_1(type=IS_LONG, value=42, refcount=1)
// $a -> zval_2(type=IS_LONG, value=43, refcount=1)
unset($c); // zval_1 is destroyed, because refcount=0
// $a -> zval_2(type=IS_LONG, value=43, refcount=1)
引用計數有個致命的問題:無法檢查並釋放迴圈引用(使用的記憶體)。為了解決這問題,PHP 使用了迴圈回收的方法。當一個 zval 的計數減一時,就有可能屬於迴圈的一部分,這時將 zval 寫入到『根緩衝區』中。當緩衝區滿時,潛在的迴圈會被打上標記並進行回收。
因為要支援迴圈回收,實際使用的 zval 的結構實際上如下:
typedef struct _zval_gc_info {
zval z;
union {
gc_root_buffer *buffered;
struct _zval_gc_info *next;
} u;
} zval_gc_info;
zval_gc_info
結構體中嵌入了一個正常的
zval 結構,同時也增加了兩個指標引數,但是共屬於同一個聯合體u
,所以實際使用中只有一個指標是有用的。buffered
指標用於儲存
zval 在根緩衝區的引用地址,所以如果在迴圈回收執行之前 zval 已經被銷燬了,這個欄位就可能被移除了。next
在回收銷燬值的時候使用,這裡不會深入。
修改動機
下面說說關於記憶體使用上的情況,這裡說的都是指在 64 位的系統上。首先,由於 str
和 obj
佔用的大小一樣, zvalue_value
這個聯合體佔用
16 個位元組(bytes)的記憶體。整個 zval
結構體佔用的記憶體是
24 個位元組(考慮到記憶體對齊),zval_gc_info
的大小是
32 個位元組。綜上,在堆(相對於棧)分配給 zval 的記憶體需要額外的 16 個位元組,所以每個 zval 在不同的地方一共需要用到 48 個位元組(要理解上面的計算方式需要注意每個指標在 64 位的系統上也需要佔用 8 個位元組)。
在這點上不管從什麼方面去考慮都可以認為 zval 的這種設計效率是很低的。比如 zval 在儲存整型的時候本身只需要 8 個位元組,即使考慮到需要存一些附加資訊以及記憶體對齊,額外 8 個位元組應該也是足夠的。
在儲存整型時本來確實需要 16 個位元組,但是實際上還有 16 個位元組用於引用計數、16 個位元組用於迴圈回收。所以說 zval 的記憶體分配和釋放都是消耗很大的操作,我們有必要對其進行優化。
從這個角度思考:一個整型資料真的需要儲存引用計數、迴圈回收的資訊並且單獨在堆上分配記憶體嗎?答案是當然不,這種處理方式一點都不好。
這裡總結一下 PHP5 中 zval 實現方式存在的主要問題:
- zval 總是單獨從堆中分配記憶體;
- zval 總是儲存引用計數和迴圈回收的資訊,即使是整型這種可能並不需要此類資訊的資料;
- 在使用物件或者資源時,直接引用會導致兩次計數(原因會在下一部分講);
- 某些間接訪問需要一個更好的處理方式。比如現在訪問儲存在變數中的物件間接使用了四個指標(指標鏈的長度為四)。這個問題也放到下一部分討論;
- 直接計數也就意味著數值只能在 zval 之間共享。如果想在 zval 和 hashtable key 之間共享一個字串就不行(除非 hashtable key 也是 zval)。
PHP7 中的 zval
在 PHP7 中 zval 有了新的實現方式。最基礎的變化就是 zval 需要的記憶體不再是單獨從堆上分配,不再自己儲存引用計數。複雜資料型別(比如字串、陣列和物件)的引用計數由其自身來儲存。這種實現方式有以下好處:
- 簡單資料型別不需要單獨分配記憶體,也不需要計數;
- 不會再有兩次計數的情況。在物件中,只有物件自身儲存的計數是有效的;
- 由於現在計數由數值自身儲存,所以也就可以和非 zval 結構的資料共享,比如 zval 和 hashtable key 之間;
- 間接訪問需要的指標數減少了。
我們看看現在 zval 結構體的定義(現在在 zend_types.h 檔案中):
struct _zval_struct {
zend_value value; /* value */
union {
struct {
ZEND_ENDIAN_LOHI_4(
zend_uchar type, /* active type */
zend_uchar type_flags,
zend_uchar const_flags,
相關推薦
PHP5和7中的zval全介紹,記憶體管理,型別,引用計數(1)
轉自:http://0x1.im/blog/php/Internal-value-representation-in-PHP-7-part-1.html
本文第一部分和第二均翻譯自Nikita Popov(nikic,PHP 官方開發組成員,柏林科技大學的學生)
雖然今天angular5發布了,但我還是吧這篇angularjs(1)+webpack的文章發出來吧哈哈哈
attrs ctrl ron 封裝 oot 註意 tro 設置 adc
本文為原創,轉載請註明出處: cnzt 文章:cnzt-p
http://www.cnblogs.com/zt-blog/p/7779384.html
寫在前面:
因
dubbo其實很簡單,就是一個遠端服務呼叫的框架(1)
dubbo專題」dubbo其實很簡單,就是一個遠端服務呼叫的框架(1)
一、dubbo是什麼?
1)本質:一個Jar包,一個分散式框架,,一個遠端服務呼叫的分散式框架。
既然是新手教學,肯定很多同學不明白什麼是分散式和遠端服務呼叫,為什麼要分散式,為什麼要遠端呼叫。我簡單畫個對比圖說明(
給定一個句子(只包含字母和空格), 將句子中的單詞位置反轉,單詞用空格分割, 單詞之間只有一個空格,前後沒有空格。 比如: (1) “hello xiao mi”-> “mi xiao hello”
題目描述
給定一個句子(只包含字母和空格), 將句子中的單詞位置反轉,單詞用空格分割, 單詞之間只有一個空格,前後沒有空格。 比如: (1) “hello xiao mi”-> “mi xiao hello”
輸入描述:
輸入資料有多組,每組佔一行,包含一個句子(句子長度小於100
java檔案操作 (1)——判別指定檔案是否存在,讀取檔案修改時間和大小,讀取文字檔案內容,向文字檔案中寫入指定內容
任務要求:
完成一個java application應用程式,判別指定路徑下指定檔名的檔案是否存在。
如果指定檔案存在,讀取並分別顯示其修改時間和檔案大小等屬性。
以文字方式開啟某一指定路徑指定檔名的文字檔案,讀取其內容並顯示。
以文字方式向某
eclipse和idea中啟動springboot專案的時候不載入控制器,訪問頁面出現404
eclipse和idea中啟動的時候不載入控制器的原因,訪問頁面出現404現象第一類第二類
現象
控制檯中顯示控制器的原因有多種,我把它們的原因分為兩大類:一類是因為註解沒有的方式錯誤或者在controll所在的java檔案沒有載入。另外一類是直接啟動sprin
NET快取框架CacheManager在混合式開發框架中的應用(1)-CacheManager的介紹和使用 基於C#的MongoDB資料庫開發應用(4)--Redis的安裝及使用
在我們開發的很多分散式專案裡面(如基於WCF服務、Web API服務方式),由於資料提供涉及到資料庫的相關操作,如果客戶端的併發數量超過一定的數量,那麼資料庫的請求處理則以爆發式增長,如果資料庫伺服器無法快速處理這些併發請求,那麼將會增加客戶端的請求時間,嚴重者可能導致資料庫服務或者應用服務直接癱瘓。快取方案
.NET快取框架CacheManager在混合式開發框架中的應用(1)-CacheManager的介紹和使用 基於C#的MongoDB資料庫開發應用(4)--Redis的安裝及使用
在我們開發的很多分散式專案裡面(如基於WCF服務、Web API服務方式),由於資料提供涉及到資料庫的相關操作,如果客戶端的併發數量超過一定的數量,那麼資料庫的請求處理則以爆發式增長,如果資料庫伺服器無法快速處理這些併發請求,那麼將會增加客戶端的請求時間,嚴重者可能導致資料庫服務或者應用服務直接癱瘓。快取方案
在程式中定義一個基類Person類,由這個基類派生出Teacher(教師)類和Leader(領導)類。採用多繼承的方式由這兩個類派生出Teacher_Leader類。並且滿足以下要求:
#include
using namespace std;
#include
#include
class Person
{
public:
Person(char* name, int age, char *gender, char * address, long phone);
void show(v
Mongodb 學習筆記 (7)通過純mongo語句,將資料庫中的秒級時間戳,轉換成Date型別,並且裝換成任意時間格式
背景:在公司專案中,我們使用了秒級時間戳,作為時間資訊。但是mongodb自支援的多種時間處理函式,只針對date型別的欄位,於是我嘗試使用了mongo語句,將整型數的時間戳,通過mongo語句,轉換成date型別。
這裡使用的是aggregate(聚合),如下:
db.t_merchant
輸入m個學生,每個學生有4門課,在主調函式中輸入學生的相關資訊,編寫三個函式: (1)求第一門課的平均分; (2)找出有兩門課以上不及格的學生,並輸出他們的學號和全部成績,要求用指標函式實現:fl
輸入m個學生,每個學生有4門課,在主調函式中輸入學生的相關資訊,編寫三個函式:
(1)求第一門課的平均分;
(2)找出有兩門課以上不及格的學生,並輸出他們的學號和全部成績,要求用指標函式實現:float*Search(float(*p)[4],int n);
(3)找出
做一個詞頻統計程式,該程式具有以下功能 基本要求: (1)可匯入任意英文文字檔案 (2)統計該英文檔案中單詞數和各單詞出現的頻率(次數),並能將單詞按字典順序輸出。 (3)將單詞及頻率寫入檔案。
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOExcep
JDK1.7中HashTable的hash為什麼對素數求餘,而不像HashMap中一樣對2的N次方求餘?
常用的hash函式是選一個數m取模(餘數),這個數在課本中推薦m是素數,但是經常見到選擇m=2^n,因為對2^n求餘數更快,並認為在key分佈均勻的情況下,key%m也是在[0,m-1]區間均勻分佈的。但實際上,key%m的分佈同m是有關的。證明如下:key%m = key - xm,即key減掉
所有和Java中代理有關的知識點都彙集於此,速進學乾貨。
開發十年,就只剩下這套架構體系了!
>>>
rhel6和7中的服務啟動以及計劃任務
周期 png ont style 查看 and ctrl 如何 編號 rhel6下
服務啟動命令
service servername (start/stop/restart/status) 啟動服務,停止服務,重啟服務,查看服務狀態
/etc/init.d/ser
bootstrap 學習筆記(1)---介紹bootstrap和柵格系統
優先 cal 圖片 應用 尺寸 文件中 lin png ice 學習前端許久,對於布置框架和響應瀏覽器用html 和javascript 寫的有點繁瑣,無意間看到這個框架,覺得挺好用的就開始學習了,但是這個框架上面有很多知識,不是所有的都要學的,故將學習筆記和覺得重點的
HTML和CSS中的居中效果(1)
htm inner height overflow n-1 txt posit splay read HTML和CSS中的居中效果
單行上下左右居中
Html:
<div class=”container”></div>
CSS:
Nordic nRF52832 學習筆記(1) 介紹,入門,與準備工作
例程 盜版 path pdf 規範 準備 但是 依然 可能 近來,物聯網已成為大勢所趨,VR與AR正方興未艾,各種手環、遙控、智能家居也在粉墨登場。技術前沿的領航者們已經快馬加鞭,各種意誌與暗示也在上傳下達。物聯網,無線通訊,移動互聯,將成為新的目標與寵兒。最近開的電賽
在Windows Server 2008 R2 Server中,上傳視頻遇到的問題(二)
content 修改 strong 報錯 con 節點 fail get tle 上一篇 在Windows Server 2008 R2 Server中,上傳視頻遇到的問題(一)中遇到上傳40M視頻報404,然後修改配置文件節點:
<httpRuntime tar
【開源分享:入門到精通ASP.NET MVC+EF6+Bootstrap】從這裏開始,一起搭框架(1)開篇介紹
strong src 擁有 ckeditor 開發 技術分享 mdi 控制 https 框架簡介
這幾年一直在做ASP.NET開發,幾年前做項目都是老老實實一行行的寫代碼,後來發現那些高手基本都會有自己積累起來的代碼庫,現在稱之為開發框架,基礎代碼不用再去堆,