PHP Object-Caching and Reference Counting

php.internals

Lars Schultz

19 years ago
Hello there... I am making my debut appearance here hoping that I am not giving offense to anyones well honed php-instincts or programming practices with my feature-request. This is what it comes down to, I guess. A Feature Request for some meta-info on the instantiated PHP-Objects. Specifically I want to know how many references are currently pointing to any one object. Since PHP's Memory Management is based on Reference-Counting this information should already be available "somewhere". Not knowing any php-source-code internals, I can't even guess at the problems which prohibit the implementation of such a feature. Nevertheless I'd like to point out an advantage to the community if the reference-count were available. I have been working on a large project for 6 years now and I got to a point where I'd like to cache certain objects singleton-like. To better explain my problem I provide this small example which surely is error-prone but should get the basic idea across. <? class MyObject { private static $instances = array(); protected $id; private function __constructor($id){ $this->id = $id; } /** * Returns an instance of the class. * If no object was instantiated with the given $id before, * it will instantiate a new object which will be associated with the given $id. * * It will also check the current memory-usage and if its used up for more than * 80% it will call the collect() method. * * @param int $id * @return MyObject */ public static function getInstance($id){ if ( !isset(self::$instances[$id]) ) { //check the used up memory wether to collect unused objects or not if ( memory_get_usage() > ini_get('memory limit')*0.8 ) self::collect(); self::$instances[$id] = new MyObject($id); } return self::$instances[$id]; } /** * Removes any object from the cache-list which is not referenced anymore. * (safe for the list and the local-reference to it) */ public static function collect(){ foreach( self::$instances as $id => $instance ) { if ( reference_count($instance) < 3 ) unset(self::$instances[$id]); } } } ?> As you can see I boldly used the function reference_count(Object) even though it doesn't exist. This is exactly what I'd need to allow my concept to work. I keep thinking of any other solutions, but up until now, I didn't find any better solution for this problem. Of course I can always ignore the memory usage and hope that it wont hit the limit...but hoping is a rather ethereal and mystic idea which doesn't go well with my preferred down-to-earth 1 plus 1 equals 2 programming. I know that a lot of you may say: Why do need to do that?...and I'd have to think for 3 days and then write for 4 days and would give up. It seems like a nice solution to me and if any of you have an idea as to how I could implement this differently, please feel free to point it out to me. Thanks for reading this far. Lars

Andreas Korthaus

19 years ago
Hi! Lars Schultz wrote:
> > As you can see I boldly used the function reference_count(Object) even > though it doesn't exist. This is exactly what I'd need to allow my > concept to work.
Some time ago I created a patch to implement a ref_count() function which works the way you suggested. But I'm not sure if it's OK/complete... and I'm not sure if something like that is really needed in the core (and I'm definetly not the person to decide on that ;-)). Anyway, I attatched a simple patch against current 5_2 branch to this mail, perhaps it helps. Best regards Andreas

Stanislav Malyshev

19 years ago
> Some time ago I created a patch to implement a ref_count() function > which works the way you suggested. But I'm not sure if it's > OK/complete... and I'm not sure if something like that is really needed > in the core (and I'm definetly not the person to decide on that ;-)).
If you talk about objects, your patch exposes wrong refcount. Your patch exposes zval refcount, however there can be a number of zvals referring to the same object, and the actual object storage keeps its own refcount, see zend_objects_API.c (provided your object is a standard Zend object). Actually, since each of these zvals can hold own refcount and we can not know what variables refer to the given object, I do not see how one could really calculate the number of different places that the object is referred to, unless you know your application really well so you could know how many places refer to the object and what exactly they are.
-- Stanislav Malyshev, Zend Products Engineer stas@zend.com http://www.zend.com/

Lars Schultz

19 years ago
Andreas Korthaus wrote:
> Some time ago I created a patch to implement a ref_count() function > which works the way you suggested. But I'm not sure if it's > OK/complete... and I'm not sure if something like that is really > needed in the core (and I'm definetly not the person to decide on that > ;-)).
Wow! This is great news...though I have no idea what to do with the patch and how to apply it to the php-source-code. The trouble is that I have to use the default php-engine because it should be runnable on any host...with some extensions. I don't guess it'd be possible to pack the functionality into en extension which could then be loaded more easily into the engine?
> Anyway, I attatched a simple patch against current 5_2 branch to this > mail, perhaps it helps.
Thanks anyway! It was an interesting read though my c and c++ knowledge is far from complete so i don't understand the workings, really...Maybe, if I feel restless, I'll have a look at the sources, though I wouldn't know where to start.

Stanislav Malyshev

19 years ago
> As you can see I boldly used the function reference_count(Object) even > though it doesn't exist. This is exactly what I'd need to allow my > concept to work. I keep thinking of any other solutions, but up until > now, I didn't find any better solution for this problem. Of course I can
Note that the engine does not guarantee you any particular value of the refcount. Passing parameters around, accessing variables, etc. may change the refcount. One thing the engine guarantees you is that once no links to the variable exist it would be destroyed (which may not happen immediately after you do unset, for example, but probably would happen "soon"). So actually figuring out if the object is referenced by any other place could prove non-trivial, depending on the application structure.
-- Stanislav Malyshev, Zend Products Engineer stas@zend.com http://www.zend.com/

Paweł Stradomski

19 years ago
Lars Schultz wrote:
> I know that a lot of you may say: Why do need to do that?...and I'd have > to think for 3 days and then write for 4 days and would give up. It > seems like a nice solution to me and if any of you have an idea as to > how I could implement this differently, please feel free to point it out > to me.
Perhaps you could use your own reference counting? Keep your "real" objects in private array and instead of returning them return a simple object which would forward all methods and properties by __get/__set/__call. The constructor would increase your internal refcount by one, destructor would decrease it.
-- Paweł Stradomski

Lars Schultz

19 years ago
>Perhaps you could use your own reference counting? >Keep your "real" objects in private array and instead of returning them return >a simple object which would forward all methods and properties by >__get/__set/__call. The constructor would increase your internal refcount by >one, destructor would decrease it. > >
I use those objects quite often as they are used to access the database to manipulate or search for related data. It wouldn't perform well and is actually not at all practicable because there are so many classes for which I'd would have to copy the interface.

Paweł Stradomski

19 years ago
On 21 jan 2007 Lars Schultz wrote:
> >Perhaps you could use your own reference counting? > >Keep your "real" objects in private array and instead of returning them > > return a simple object which would forward all methods and properties by > > __get/__set/__call. The constructor would increase your internal refcount > > by one, destructor would decrease it. > > I use those objects quite often as they are used to access the database > to manipulate or search for related data. It wouldn't perform well and > is actually not at all practicable because there are so many classes for > which I'd would have to copy the interface.
I don't see any place where you'd have to change the interface - except for the class which contains that private array; you can use the name of your current class (MyObject)as the name of this new "forwarding" class and make a new name for the class which would be a copy of your current class (eg. MyObject_private). Therefore in all function definitions required class specification doesn't need to change. Well, perhaps you would have to change some "factory" class if you use one, but that should be a one place - if you kept your code clean. I think this solution is better, as you don't have to rely on internal refcounts, which can be implemented in another way (or dropped altogether) in future releases.
-- Paweł Stradomski