이 글에서는 PHP 소스 코드 34에 대해 주로 소개합니다. PHP5.3에 새로 추가된 가비지 컬렉션 메커니즘은 이제 특정 참고 가치가 있어 도움이 필요한 모든 사람들과 공유합니다.
PHP 소스코드에 대해 간단히 이야기해보자 34: PHP5.3에 새로 추가된 가비지 컬렉션 메커니즘(Garbage Collection)
이전 글에서는 PHP 소스코드에 대해 이야기해보자. 33: PHP5.3의 새로운 가비지 수집 메커니즘(Garbage Collection) 기본에서는 가비지 수집 메커니즘에 대한 몇 가지 기본 지식을 소개합니다. 오늘 우리는 초기화를 살펴보고 가비지 버퍼와 가비지 수집 프로세스를 추가합니다.
공식 문서를 보려면 Garbage Collection을 클릭하세요
중국어 버전 주소: http://docs.php.net/manual/zh/features.gc.php
【초기화】#🎜 🎜# zend/zend_gc.c의 121번째 줄에는 gc의 초기화를 구현하는 gc_init 함수가 있습니다. 코드는 다음과 같습니다:
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); }}
124번째 줄은 malloc을 직접 호출하여 10,000gc_root_buffer 메모리를 할당합니다.
Line 125는 전역 변수 last_unused를 gc 버퍼의 끝 위치로 설정합니다.
126행은 전체 가비지 수집 메커니즘을 재설정합니다. 코드는 다음과 같이 zend/zend_gc.c의 88행에서 시작합니다.
ZEND_API void gc_reset(TSRMLS_D){ GC_G(gc_runs) = 0; GC_G(collected) = 0; #if GC_BENCH GC_G(root_buf_length) = 0; GC_G(root_buf_peak) = 0; GC_G(zval_possible_root) = 0; GC_G(zobj_possible_root) = 0; GC_G(zval_buffered) = 0; GC_G(zobj_buffered) = 0; GC_G(zval_remove_from_buffer) = 0; GC_G(zobj_remove_from_buffer) = 0; GC_G(zval_marked_grey) = 0; GC_G(zobj_marked_grey) = 0;#endif GC_G(roots).next = &GC_G(roots); GC_G(roots).prev = &GC_G(roots); if (GC_G(buf)) { GC_G(unused) = NULL; GC_G(first_unused) = GC_G(buf); GC_G(zval_to_free) = NULL; } else { GC_G(unused) = NULL; GC_G(first_unused) = NULL; GC_G(last_unused) = NULL; }}
106~107행은 이중 연결 리스트의 헤드 노드의 이전 노드와 다음 노드가 자신을 가리키도록 설정합니다.
구현 코드는 zend/zend.c의 93번째 라인에 다음과 같습니다:
STD_ZEND_INI_BOOLEAN("zend.enable_gc","1",ZEND_INI_ALL,OnUpdateGCEnabled, gc_enabled, zend_gc_globals, gc_globals)
static ZEND_INI_MH(OnUpdateGCEnabled) /* {{{ */{ OnUpdateBool(entry, new_value, new_value_length, mh_arg1, mh_arg2, mh_arg3, stage TSRMLS_CC); if (GC_G(gc_enabled)) { gc_init(TSRMLS_C); } return SUCCESS;}
[_zval_ptr_dtor] -> [GC_ZVAL_CHECK_POSSIBLE_ROOT()] -> gc_zval_possible_root() ]#🎜 🎜#gc_zval_check_possible_root() 함수에서는 배열과 객체에 대해 가비지 수집 작업만 수행됩니다
gc_zval_possible_root 함수의 코드는 다음과 같습니다
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); 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); 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); } }}
Lines 142 ~145 객체 노드를 직접 처리하고 더 이상 후속 작업을 수행하지 않습니다.
149행은 노드가 보라색으로 표시되었는지 여부를 결정합니다. 이는 더 이상 노드 버퍼에 추가되지 않습니다. 노드가 버퍼 작업에 추가만 수행하는지 확인하는 것입니다.
150행은 노드의 색상을 보라색으로 표시하여 이 노드가 버퍼에 추가되었으며 다음에 추가할 필요가 없음을 나타냅니다.
알아보기 153~157번째 줄에는 새 노드의 위치입니다. 버퍼가 가득 차면 가비지 수집 작업이 수행됩니다.
176~184번째 줄은 버퍼가 위치한 이중 연결 리스트에 새로운 노드를 추가합니다.
[가비지 수집 프로세스]
gc_zval_possible_root 함수에서 버퍼가 가득 차면 프로그램은 gc_collect_cycles 함수를 호출하여 가비지 수집 작업을 수행합니다. zend/zend_gc.c 파일의 615번째 줄부터 구현 코드는 다음과 같습니다: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;}
622행은 가비지 수집 작업이 진행 중인지 확인합니다. 진행 중이면 직접 반환합니다.
625~627행 가비지 수집 작업 수에 1을 더하고 사용 가능 목록을 초기화한 후 설정합니다. gc_active를 1로 설정하여 가비지 반환이 진행 중임을 나타냅니다.
Line 629 이것이 알고리즘의 C단계입니다. 알고리즘은 다시 한번 각 루트 노드에 대해 깊이 우선 검색을 사용하고 참조 개수가 0이면 변수 컨테이너가 0보다 큰 경우 작업이 수행됩니다. 이 시점에서 깊이 우선 탐색을 사용하여 참조 카운트를 1씩 감소시키는 작업이 재개된 다음 참조 카운트가 1만큼 증가합니다.
라인 630의 알고리즘의 마지막 단계 D, 알고리즘이 순회합니다. 거기에서 변수 컨테이너 루트(zval 루트)를 제거하는 동시에 이전 단계에서 흰색으로 표시된 변수 컨테이너가 있는지 확인합니다. [gc_collect_cycles() -> gc_collect_roots() -> zval_collect_white() ]에서 흰색으로 표시된 노드가 전역 변수 zval_to_free 목록에 추가됩니다.
라인. 632~633 전역 변수 free_list 및 next_to_free를 해당 임시 변수에 저장하고 마지막에 현재 상태로 복원됩니다.
634~635행은 지워야 할 목록을 초기화하고 zval 목록을 지웁니다. 가비지 수집 작업 상태를 비활성 상태로 설정합니다.
639~655번째 줄은 소멸자를 처음으로 호출하고 지워진 변수의 수를 계산합니다.
657~678번째 줄의 변수 지우기#🎜🎜 # 682~686행 메모리 해제
687~689행은 가비지 번호 통계 처리 및 free_list 및 next_to_free 변수 복원# 🎜🎜#
위 내용은 이 글의 전체 내용입니다. 더 많은 관련 내용은 PHP 중국어 홈페이지를 주목해주세요
관련 추천:
PHP 소스 코드에 대한 간략한 토론 33: PHP5.3의 새로운 가비지 수집 메커니즘(가비지 수집)의 기본
PHP 소스 코드에 대한 간략한 토론 32: PHP의 emalloc/efree 레이어 및 힙 메모리 풀) 레이어
PHP 소스 코드 29에 대한 간략한 토론: 인터페이스 상속에 대하여
위 내용은 PHP 소스 코드 34에 대한 간략한 논의: PHP5.3에 새로 추가된 가비지 수집 메커니즘(Garbage Collection)의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!