前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >PHP - php7基本变量与内存管理机制

PHP - php7基本变量与内存管理机制

原创
作者头像
stark张宇
发布2023-02-07 10:58:40
4340
发布2023-02-07 10:58:40
举报
文章被收录于专栏:stark张宇stark张宇

概述

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

变量实现

1. 解密zval

zval 底层结构:

代码语言:txt
复制
struct_zval_struct {
    zend_value value; //8个字节
    union u1; //4个字节
    union u2; //4个字节
}

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

代码语言:txt
复制
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)

代码语言:txt
复制
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加上了长度。

代码语言:php
复制
$value1 = 'stark';
$value2 = $value1;
$value2 = 'zcc';

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

3.字符串的引用类型

代码语言:txt
复制
struct _zend_reference {
	zend_refcounted_h gc;
	zval              val;
};

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

代码语言:txt
复制
$a = 'hello';
$b = &$a;
var_dump($a,$b);

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

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

执行结果:

代码语言:txt
复制
[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

代码语言:txt
复制
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大小内存的呢?

代码语言:txt
复制
void *ptr=malloc(size);
free(ptr);

php7内存接口

代码语言:txt
复制
void *ptr=_emalloc(size);
_efree(ptr);

1.Small内存的管理

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

  • chunk: 2MB 大小的内存
  • page :4KB大小的内存
代码语言:txt
复制
#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)

2. Chunk的内存对齐

关于chunk对齐的算法

代码语言:txt
复制
/**********/
/* 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;
	}
}

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 概述
  • 变量实现
    • 1. 解密zval
      • 2.写时复制(Copy On Write)
        • 3.字符串的引用类型
        • 内存管理
          • 1.Small内存的管理
            • 2. Chunk的内存对齐
            领券
            问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档