More generally, d开发者_运维知识库oes anyone know where the way APC works internally is documented?
The short answer is yes, it appears to free and reclaim memory. Below I have listed the main functions involved, descending further into the call stack as we go:
apc_delete
apc_cache_user_delete
remove_slot
free_slot
apc_sma_free
sma_deallocate
apc_delete
// taken from the file php_apc.c
// http://php-apc.sourcearchive.com/documentation/3.0.18/php__apc_8c-source.html
/* {{{ proto mixed apc_delete(string key)
*/
PHP_FUNCTION(apc_delete) {
char *strkey;
int strkey_len;
if(!APCG(enabled)) RETURN_FALSE;
if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &strkey, &strkey_len) == FAILURE) {
return;
}
if(!strkey_len) RETURN_FALSE;
if(apc_cache_user_delete(apc_user_cache, strkey, strkey_len + 1)) {
RETURN_TRUE;
} else {
RETURN_FALSE;
}
}
/* }}} */
apc_cache_user_delete
// taken from the file apc_cache.c
// http://php-apc.sourcearchive.com/documentation/3.0.18/apc__cache_8c-source.html
/* {{{ apc_cache_user_delete */
int apc_cache_user_delete(apc_cache_t* cache, char *strkey, int keylen)
{
slot_t** slot;
LOCK(cache);
slot = &cache->slots[string_nhash_8(strkey, keylen) % cache->num_slots];
while (*slot) {
if (!memcmp((*slot)->key.data.user.identifier, strkey, keylen)) {
remove_slot(cache, slot);
UNLOCK(cache);
return 1;
}
slot = &(*slot)->next;
}
UNLOCK(cache);
return 0;
}
/* }}} */
remove_slot
// taken from the file apc_cache.c
// http://php-apc.sourcearchive.com/documentation/3.0.18/apc__cache_8c-source.html
/* {{{ remove_slot */
static void remove_slot(apc_cache_t* cache, slot_t** slot)
{
slot_t* dead = *slot;
*slot = (*slot)->next;
cache->header->mem_size -= dead->value->mem_size;
cache->header->num_entries--;
if (dead->value->ref_count <= 0) {
free_slot(dead);
}
else {
dead->next = cache->header->deleted_list;
dead->deletion_time = time(0);
cache->header->deleted_list = dead;
}
}
/* }}} */
free_slot
// taken from the file apc_cache.c
// http://php-apc.sourcearchive.com/documentation/3.0.18/apc__cache_8c-source.html
/* {{{ free_slot */
static void free_slot(slot_t* slot)
{
if(slot->value->type == APC_CACHE_ENTRY_USER) {
apc_sma_free((char *)slot->key.data.user.identifier);
} else if(slot->key.type == APC_CACHE_KEY_FPFILE) {
apc_sma_free((char *)slot->key.data.fpfile.fullpath);
}
apc_cache_free_entry(slot->value);
apc_sma_free(slot);
}
/* }}} */
apc_sma_free
// taken from the file apc_sma.c
// http://php-apc.sourcearchive.com/documentation/3.0.18/apc__sma_8c-source.html
/* {{{ apc_sma_free */
void apc_sma_free(void* p)
{
int i;
size_t offset;
size_t d_size;
TSRMLS_FETCH();
if (p == NULL) {
return;
}
assert(sma_initialized);
for (i = 0; i < sma_numseg; i++) {
LOCK(((header_t*)sma_shmaddrs[i])->sma_lock);
offset = (size_t)((char *)p - (char *)(sma_shmaddrs[i]));
if (p >= sma_shmaddrs[i] && offset < sma_segsize) {
d_size = sma_deallocate(sma_shmaddrs[i], offset);
if (APCG(mem_size_ptr) != NULL) { *(APCG(mem_size_ptr)) -= d_size; }
UNLOCK(((header_t*)sma_shmaddrs[i])->sma_lock);
#ifdef VALGRIND_FREELIKE_BLOCK
VALGRIND_FREELIKE_BLOCK(p, 0);
#endif
return;
}
UNLOCK(((header_t*)sma_shmaddrs[i])->sma_lock);
}
apc_eprint("apc_sma_free: could not locate address %p", p);
}
/* }}} */
sma_deallocate
// taken from the file apc_sma.c
// http://php-apc.sourcearchive.com/documentation/3.0.18/apc__sma_8c-source.html
/* {{{ sma_deallocate: deallocates the block at the given offset */
static size_t sma_deallocate(void* shmaddr, size_t offset)
{
header_t* header; /* header of shared memory segment */
block_t* cur; /* the new block to insert */
block_t* prv; /* the block before cur */
block_t* nxt; /* the block after cur */
size_t size; /* size of deallocated block */
offset -= ALIGNWORD(sizeof(struct block_t));
assert(offset >= 0);
/* find position of new block in free list */
cur = BLOCKAT(offset);
prv = BLOCKAT(ALIGNWORD(sizeof(header_t)));
CHECK_CANARY(cur);
#ifdef __APC_SMA_DEBUG__
CHECK_CANARY(prv);
fprintf(stderr, "free(%p, size=%d,id=%d)\n", cur, (int)(cur->size), cur->id);
#endif
while (prv->next != 0 && prv->next < offset) {
prv = BLOCKAT(prv->next);
#ifdef __APC_SMA_DEBUG__
CHECK_CANARY(prv);
#endif
}
CHECK_CANARY(prv);
/* insert new block after prv */
cur->next = prv->next;
prv->next = offset;
#ifdef __APC_SMA_DEBUG__
CHECK_CANARY(cur);
cur->id = -1;
#endif
/* update the block header */
header = (header_t*) shmaddr;
header->avail += cur->size;
size = cur->size;
if (((char *)prv) + prv->size == (char *) cur) {
/* cur and prv share an edge, combine them */
prv->size += cur->size;
prv->next = cur->next;
RESET_CANARY(cur);
cur = prv;
}
nxt = BLOCKAT(cur->next);
if (((char *)cur) + cur->size == (char *) nxt) {
/* cur and nxt shared an edge, combine them */
cur->size += nxt->size;
cur->next = nxt->next;
#ifdef __APC_SMA_DEBUG__
CHECK_CANARY(nxt);
nxt->id = -1; /* assert this or set it ? */
#endif
RESET_CANARY(nxt);
}
header->nfoffset = 0; /* Reset the next fit search marker */
return size;
}
/* }}} */
The core files can be found here: http://php-apc.sourcearchive.com/documentation/3.0.18/files.html
Yes, but: If you do lots of store and delete memory gets fragmented. And apc doesn't have a way to "defragment" memory and after some time it might not find enough free space where the new var fits in. So the memory is free but can't be used.
If that happens apc usually deletes everything. (that can be configured)
精彩评论