| 
        php.net | support | documentation | report a bug | advanced search | search howto | statistics | random bug | login | 
  [2007-07-13 10:44 UTC] askalski at gmail dot com
 Description:
------------
Tested APC 3.0.14 with PHP 5.2.1 and PHP 4.4.7, as an apache2 (2.2.3) module.
Cached functions with parameters that default to array() are being copied from shared memory to the local process heap space upon retrieval from cache.
We are in the process of evaluating an upgrade from PHP4/mmcache to PHP5/APC, and our application is seeing a 40% increase in memory usage because of this issue.  (As a comparison, I built a modified version of APC that doesn't copy upon seeing a CONSTANT_ARRAY, and memory usage dropped back down to expected levels.)
Could this be implemented in a more memory-efficient manner?
Also, I would appreciate a small test case that demonstrates why CONSTANT_ARRAY opcodes can't be executed directly out of shared memory?  I tried several ways to break my modified no-copy APC, but couldn't force any aberrant behavior.
Thanks in advance.
APC settings in php.ini:
    [APC]
    apc.enabled = 1
    apc.shm_segments = 1
    apc.shm_size = 10
    apc.cache_by_default = On
    apc.mmap_file_mask = "/apc.shm.XXXXXX"
Reproduce code:
---------------
<?php
// function test($a = false)
function test($a = array())
{
    $b=1; $b=1; $b=1; $b=1; $b=1; $b=1; $b=1; $b=1;
    // repeat the previous line a bunch of times to
    // increase the function's memory footprint
}
echo memory_get_usage(), "\n";
?>
Expected result:
----------------
I expect function test($a = false) and function test($a = array()) to have similar memory_get_usage() numbers when served from cache (i.e. the second and subsequent page loads).
Actual result:
--------------
Only function test($a = false) shows a significant drop in memory usage.  The version with function test($a = array()) does not.
PatchesPull RequestsHistoryAllCommentsChangesGit/SVN commits             
             | 
    |||||||||||||||||||||||||||
            
                 
                Copyright © 2001-2025 The PHP GroupAll rights reserved.  | 
        Last updated: Tue Nov 04 14:00:01 2025 UTC | 
Thank you for your prompt reply. Knowing that it's refcount related allowed me to reproduce the SEGV with my modified (needcopy=0) version of APC. Here's the test script I ran many times concurrently: <?php function x($a = array(1, 2, 3, 4, 5)) { } for ($i = 0; $i < 100000; $i++) { x(); } ?> This crashed PHP 4.4.7, but not PHP 5.2.1. (It also turns out that mmcache crashes on this - heh.) This leaves two questions in my mind: - Is it still an issue as of the most recent versions of PHP5? - Is there a workaround that doesn't require copying the entire function into local process space?Since I haven't heard anything about this in over a month, I'll paste the patch I've been using to work around the memory issue. The idea here is that an empty CONSTANT_ARRAY shouldn't force a deep copy, since the race condition is on array members (empty array = no members = no crash). --- APC-3.0.14-orig/apc_compile.c 2007-04-02 19:05:30.000000000 -0400 +++ APC-3.0.14/apc_compile.c 2007-07-16 12:22:41.000000000 -0400 @@ -1235,7 +1235,8 @@ #endif case ZEND_RECV_INIT: if(zo->op2.op_type == IS_CONST && - zo->op2.u.constant.type == IS_CONSTANT_ARRAY) { + zo->op2.u.constant.type == IS_CONSTANT_ARRAY && + zo->op2.u.constant.value.ht->nNumOfElements) { if(flags != NULL) { flags->deep_copy = 1; } @@ -1243,9 +1244,11 @@ break; default: if((zo->op1.op_type == IS_CONST && - zo->op1.u.constant.type == IS_CONSTANT_ARRAY) || + zo->op1.u.constant.type == IS_CONSTANT_ARRAY && + zo->op1.u.constant.value.ht->nNumOfElements) || (zo->op2.op_type == IS_CONST && - zo->op2.u.constant.type == IS_CONSTANT_ARRAY)) { + zo->op2.u.constant.type == IS_CONSTANT_ARRAY && + zo->op2.u.constant.value.ht->nNumOfElements)) { if(flags != NULL) { flags->deep_copy = 1; } @@ -2010,9 +2013,11 @@ zo = &src->opcodes[j]; if( ((zo->op1.op_type == IS_CONST && - zo->op1.u.constant.type == IS_CONSTANT_ARRAY)) || + zo->op1.u.constant.type == IS_CONSTANT_ARRAY) && + zo->op1.u.constant.value.ht->nNumOfElements) || ((zo->op2.op_type == IS_CONST && - zo->op2.u.constant.type == IS_CONSTANT_ARRAY))) { + zo->op2.u.constant.type == IS_CONSTANT_ARRAY) && + zo->op2.u.constant.value.ht->nNumOfElements)) { needcopy = 1; } }