IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> PHP知识库 -> php源码02 -基本变量与内存管理机制 -> 正文阅读

[PHP知识库]php源码02 -基本变量与内存管理机制

概述

这是源码php7系列的第二篇文章,主要介绍变量的机制和内存的管理,我相信学习源码是对代码整体提升的有效手段,话不多说,开始吧!

变量实现

1. 解密zval

zval 底层结构:

struct_zval_struct {
    zend_value value; //8个字节
    union u1; //4个字节
    union u2; //4个字节
}

对于vue来说是一个联合体,zval一共16个字节,u1 4个字节,u2四个字节,value结构体如下:

typedef union _zend_value {
	zend_long         lval;				/* long value */
	double            dval;				/* double value */
	zend_refcounted  *counted;
	zend_string      *str;
	zend_array       *arr;
	zend_object      *obj;
	zend_resource    *res;
	zend_reference   *ref;
	zend_ast_ref     *ast;
	zval             *zv;
	void             *ptr;
	zend_class_entry *ce;
	zend_function    *func;
	struct {
		uint32_t w1;
		uint32_t w2;
	} ww;
} zend_value;

虽然PHP属于弱类型语言,但是在底层实现中还是要区分类型的,因为类型里有天然的长度,类型引势内存的长度。

底层做了很多类型转化的处理,让我们不用关心php的类型和长度,这也是php开发高效的原因之一。

变量知识点:

  • value、u1、u2都是联合体,在底层是要区分类型的
  • u2里面有个重要的变量next,next会在数组中解决冲突使用

2.写时复制(Copy On Write)

struct _zend_string {
	zend_refcounted_h gc;
	zend_ulong        h;                /* hash value */
	size_t            len;
	char              val[1];
};

zend_refcounted_h 作用是string类型的引用计数的结构体,h是字符串对应的hash值,它后面会用到数组里,len代表字符串的长度,char是字符串的值,因为C言语中字符串遇到\0就会自动结束,二进制是不安全的,所以php加上了长度。

$value1 = 'stark';
$value2 = $value1;
$value2 = 'zcc';

php的写时复制是这样发生的,如果把 v a l u e 1 赋 值 给 value1赋值给 value1value2,两个变量指向的是同一个物理内存地址,存在硬盘上的某一个块里,也许地址是0x7fff5e01c00,当$value2赋值新的值时,zend_refcounted_h引用计数减一,zcc存入新的地址。可以看我之前的文章

3.字符串的引用类型

struct _zend_reference {
	zend_refcounted_h gc;
	zval              val;
};

可以跟着代码执行一下,看看你心里的预期和实际打印出的值是否一致

$a = 'hello';
$b = &$a;
var_dump($a,$b);

$b = 'stark';
var_dump($a,$b);

unset($b);
var_dump($a,$b);

执行结果:

[root@dd2065d03db8 code]# /usr/local/php7.1.0/bin/php refer.php
string(5) "hello"
string(5) "hello"
string(5) "stark"
string(5) "stark"
string(5) "stark"
NULL

源码中的数组HashTable

struct _zend_array {
	zend_refcounted_h gc;
	union {
		struct {
			ZEND_ENDIAN_LOHI_4(
				zend_uchar    flags,
				zend_uchar    nApplyCount,
				zend_uchar    nIteratorsCount,
				zend_uchar    consistency)
		} v;
		uint32_t flags;
	} u;
	uint32_t          nTableMask;
	Bucket           *arData;
	uint32_t          nNumUsed;
	uint32_t          nNumOfElements;
	uint32_t          nTableSize;
	uint32_t          nInternalPointer;
	zend_long         nNextFreeElement;
	dtor_func_t       pDestructor;
};

nTableMask是计算数组的索引值,*arData存储数组里的key=>value的键值对,nNumUsed表示已经使用的空间,nNumOfElements真正的元素个数,nTableSize是arData的大小,nTableSize默认大小是8字节,内存不够每次扩容都x2,以此类推。

内存管理

在malloc申请内存时声明了size大小,但是回收时没有传size,怎么做到准确释放size大小内存的呢?

void *ptr=malloc(size);
free(ptr);

php7内存接口

void *ptr=_emalloc(size);
_efree(ptr);

1.Small内存的管理

内存的基本概念:chunk、page、各种规格的内存。

  • chunk: 2MB 大小的内存
  • page :4KB大小的内存
#define ZEND_MM_CHUNK_SIZE (2 * 1024 * 1024)               /* 2 MB  */
#define ZEND_MM_PAGE_SIZE  (4 * 1024)                      /* 4 KB  */
#define ZEND_MM_PAGES      (ZEND_MM_CHUNK_SIZE / ZEND_MM_PAGE_SIZE)  /* 512 */

内存规格

  • 内存预分配:使用mmap分配chunk

内存分类:

  • 1.Small(30种规格) (size <= 3KB)
  • 2.Large (3KB < size <= 2MB-4KB)
  • 3.Huge(size > 2MB-4KB)

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-xbOuyCiU-1645874280990)(https://note.youdao.com/yws/res/16156/WEBRESOURCEd3870cc7b7e2858d7dbc5267989a84b5)]

2. Chunk的内存对齐

关于chunk对齐的算法

/**********/
/* Chunks */
/**********/

static void *zend_mm_chunk_alloc_int(size_t size, size_t alignment)
{
	void *ptr = zend_mm_mmap(size);

	if (ptr == NULL) {
		return NULL;
	} else if (ZEND_MM_ALIGNED_OFFSET(ptr, alignment) == 0) {
#ifdef MADV_HUGEPAGE
	    madvise(ptr, size, MADV_HUGEPAGE);
#endif
		return ptr;
	} else {
		size_t offset;

		/* chunk has to be aligned */
		zend_mm_munmap(ptr, size);
		ptr = zend_mm_mmap(size + alignment - REAL_PAGE_SIZE);
#ifdef _WIN32
		offset = ZEND_MM_ALIGNED_OFFSET(ptr, alignment);
		zend_mm_munmap(ptr, size + alignment - REAL_PAGE_SIZE);
		ptr = zend_mm_mmap_fixed((void*)((char*)ptr + (alignment - offset)), size);
		offset = ZEND_MM_ALIGNED_OFFSET(ptr, alignment);
		if (offset != 0) {
			zend_mm_munmap(ptr, size);
			return NULL;
		}
		return ptr;
#else
		offset = ZEND_MM_ALIGNED_OFFSET(ptr, alignment);
		if (offset != 0) {
			offset = alignment - offset;
			zend_mm_munmap(ptr, offset);
			ptr = (char*)ptr + offset;
			alignment -= offset;
		}
		if (alignment > REAL_PAGE_SIZE) {
			zend_mm_munmap((char*)ptr + size, alignment - REAL_PAGE_SIZE);
		}
# ifdef MADV_HUGEPAGE
	    madvise(ptr, size, MADV_HUGEPAGE);
# endif
#endif
		return ptr;
	}
}
  PHP知识库 最新文章
Laravel 下实现 Google 2fa 验证
UUCTF WP
DASCTF10月 web
XAMPP任意命令执行提升权限漏洞(CVE-2020-
[GYCTF2020]Easyphp
iwebsec靶场 代码执行关卡通关笔记
多个线程同步执行,多个线程依次执行,多个
php 没事记录下常用方法 (TP5.1)
php之jwt
2021-09-18
上一篇文章      下一篇文章      查看所有文章
加:2022-02-28 15:08:53  更:2022-02-28 15:09:01 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/23 11:08:48-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码