>  기사  >  백엔드 개발  >  낮은 메모리 사용률과 약한 유형의 PHP 배열의 예

낮은 메모리 사용률과 약한 유형의 PHP 배열의 예

黄舟
黄舟원래의
2017-08-10 10:19:111077검색

이 글은 주로 낮은 메모리 활용도와 약한 유형의 PHP 배열에 대한 자세한 해석을 소개합니다. 관심 있는 친구들이 참고할 수 있습니다.

이 이틀의 작업은 예정보다 일찍 완료되었습니다. 잠시 쉬면서 PHP를 심도 있게 배울 수 있습니다. 사실 원래는 PHP 성능 최적화에 관련된 것을 배우고 싶었는데, 인터넷에서 "PHP 배열 메모리 활용도가 낮습니다. C 언어로 100MB 메모리 배열을 사용하려면 PHP에서 1G가 필요합니다."라는 문장을 보고 충격을 받았습니다. PHP는 실제로 그렇게 많은 메모리를 소비합니까? 그래서 이번 기회에 PHP의 데이터형 구현에 대해 알아보았습니다.

먼저 테스트해 보겠습니다.


<?php 
  echo memory_get_usage() , &#39;<br>&#39;; 
  $start = memory_get_usage(); 
  $a = Array(); 
  for ($i=0; $i<1000; $i++) { 
   $a[$i] = $i + $i; 
  } 
  $end = memory_get_usage(); 
  echo memory_get_usage() , &#39;<br>&#39;; 
  echo &#39;argv:&#39;, ($end - $start)/1000 ,&#39;bytes&#39; , &#39;<br>&#39;;

결과:

353352
437848
argv: 84.416바이트

1000개 요소의 정수 배열은 메모리를 소비합니다(4378 48 - 353352) 바이트, 약 82KB, 이는 각 요소는 84바이트의 메모리를 차지합니다. C 언어에서 int는 4바이트를 차지하며 이는 전체적으로 20배의 차이입니다.

하지만 인터넷에서는 memory_get_usage()가 반환한 결과가 모두 배열 점유가 아니라 PHP 자체의 일부 구조를 포함한다고 합니다. 따라서 다른 방법을 시도하고 PHP 내장 함수를 사용하여 배열을 생성하세요.


<?php 
  $start = memory_get_usage(); 
  $a = array_fill(0, 10000, 1); 
  $end = memory_get_usage(); //10k elements array; 
  echo &#39;argv:&#39;, ($end - $start )/10000,&#39;byte&#39; , &#39;<br>&#39;;

출력은 다음과 같습니다.

argv:54.5792byte

는 이전보다 약간 나아졌지만 여전히 54바이트로 실제로 약 10배 더 나쁩니다.

이유는 PHP의 기본 구현에서 시작되어야 합니다. PHP는 int, double, string 등에 관계없이 약한 유형의 언어이므로 통합된 '$'로 모든 문제를 해결할 수 있습니다. PHP의 최하위 계층은 C 언어로 구현됩니다. 각 변수는 자세한 정의가 다음과 같은 zval 구조에 해당합니다. PHP는 변수의 값을 zvalue_value 유형으로 저장하기 위해 결합 구조를 사용합니다. zval은 다음과 같이 정의된 공용체입니다.


typedef struct _zval_struct zval; 
struct _zval_struct { 
  /* Variable information */ 
  zvalue_value value;   /* The value 1 12字节(32位机是12,64位机需要8+4+4=16) */ 
  zend_uint refcount__gc; /* The number of references to this value (for GC) 4字节 */ 
  zend_uchar type;    /* The active type 1字节*/ 
  zend_uchar is_ref__gc; /* Whether this value is a reference (&) 1字节*/ 
};

공용체 유형이 차지하는 메모리 크기는 가장 큰 멤버가 차지하는 데이터 공간에 따라 결정됩니다. zvalue_value에서는 str 구조체의 int가 4바이트를 차지하고 char 포인터가 4바이트를 차지하므로 전체 zvalue_value가 차지하는 메모리는 8바이트이다.


zval의 크기는 8 + 4 + 1 + 1 = 14바이트입니다.


zvalue_value에도 HashTable이 있다는 점에 주목하세요. zval에서는 배열, 문자열 및 객체에도 추가 저장 구조가 필요합니다. 배열의 저장 구조는 HashTable입니다.


HashTable 정의는 다음을 제공합니다.


typedef union _zvalue_value { 
  long lval;         /* long value */ 
  double dval;        /* double value */ 
  struct {          /* string value */ 
    char *val; 
    int len; 
  } str;  
  HashTable *ht;       /* hash table value */ 
  zend_object_value obj;   /*object value */ 
} zvalue_value;

테이블의 크기와 포함된 요소 수를 기록하는 여러 속성 변수 외에도 Bucket은 여러 번 사용됩니다. Bucket이 정의되는 방법:


typedef struct _hashtable { 
   uint nTableSize; //表长度,并非元素个数 
   uint nTableMask;//表的掩码,始终等于nTableSize-1 
   uint nNumOfElements;//存储的元素个数 
   ulong nNextFreeElement;//指向下一个空的元素位置 
   Bucket *pInternalPointer;//foreach循环时,用来记录当前遍历到的元素位置 
   Bucket *pListHead; 
   Bucket *pListTail; 
   Bucket **arBuckets;//存储的元素数组 
   dtor_func_t pDestructor;//析构函数 
   zend_bool persistent;//是否持久保存。从这可以发现,PHP数组是可以实现持久保存在内存中的,而无需每次请求都重新加载。 
   unsigned char nApplyCount; 
   zend_bool bApplyProtection; 
} HashTable;

Linked List와 마찬가지로 Bucket은 특정 데이터와 포인터가 있는 Linked List 노드인 반면, HashTable은 Bucket 요소의 문자열을 저장하는 배열입니다. PHP에서 다차원 배열의 구현은 버킷에 저장된 또 다른 HashTable입니다.


HashTable이 39바이트, Bucket이 33바이트를 차지한다고 계산해 보세요. 빈 배열은 14 + 39 + 33 = 86바이트를 차지합니다. Bucket 구조는 33바이트가 필요하며 키 길이가 4바이트보다 긴 부분은 Bucket 끝에 추가되며 요소 값은 zval 구조일 가능성이 높습니다. 또한 각 배열에는 Bucket 포인터가 할당됩니다. arBuckets가 가리키는 배열은 말할 수 없지만 각 추가 요소에는 포인터가 필요하지만 상황은 더 나쁠 수 있습니다. 이는 하나의 배열 요소가 54바이트를 차지하게 된다는 것을 계산하는데, 이는 위의 추정과 거의 동일합니다.


공간 관점에서 볼 때 작은 배열은 평균적으로 비용이 많이 듭니다. 물론 스크립트는 많은 수의 작은 배열로 채워지지 않으며 더 작은 공간 비용으로 프로그래밍 속도를 얻을 수 있습니다. 그러나 배열을 컨테이너로 사용하면 이야기가 달라집니다. 실제 응용 프로그램에서는 요소가 많은 다차원 배열을 자주 접하게 됩니다. 예를 들어, 10k 요소의 1차원 배열은 약 540k의 메모리를 소비하는 반면 10k의 2차원 배열은 실제로 23M을 소비합니다. 작은 배열은 실제로 그럴 가치가 없습니다.

위 내용은 낮은 메모리 사용률과 약한 유형의 PHP 배열의 예의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!

성명:
본 글의 내용은 네티즌들의 자발적인 기여로 작성되었으며, 저작권은 원저작자에게 있습니다. 본 사이트는 이에 상응하는 법적 책임을 지지 않습니다. 표절이나 침해가 의심되는 콘텐츠를 발견한 경우 admin@php.cn으로 문의하세요.