There was a need to create a class object in the cycle. The cycle can be 1000 or more iterations. I'm having doubts about the use of memory. Therefore, the question of whether to do so?. Ready to hear the pros and cons of using this design. In advance thank everyone for answers.
Code example:
foreach($entities as $entity) {
$object = new $class($entity);
$object->doSomething();
}
Unless your class produce some trash in global space it won't affect (much) overall memory usage. Each instance will be disposed right at the end of cycle.
This wont affect memory. After each loop the new $object will fall out of scope and will be eligible for garbage collection. PHP is then free to remove the object from memory.
Related
I am currently working on fixing a bug (in my software) that i tracked down to what i think is a singleton in my collection. Every time i loop over the collection i reset the position to 0 , so as far as i understand every loop that is running the same object will be affected.
class Collection implements \Iterator {..}
$coll = new Collection();
foreach ($coll as $a) { // loop A
foreach($coll as $b) { // loop B
// triggers rewind and affects loopA
}
}
i am pretty sure i am missing something big or maybe i am trying something that should be solved differently? should i create a copy for every loop do? but i do want the object to share the same data but just not the position? should i create a unique key for every rewind and store different position for each in an array of positions? (sounds doable but .. wrong? there must be a better way.. right?)
i found this 12y thread that describes the same problem but i hope something has changed in that time? Do people just dont do it and use arrays instead? have other languages the same problem?
nested foreach with iterator interface
I'm using laravel 5.8 and I'm in a situation where I need to repeatedly check if a variable exists in session to return and if not, use a new instance of a class and return that.
Depending on the variable I'm looking for in session, it'll also need a different type of class. So what I've done is simply pass in a new instance of what ever object I need and use that if needed.
private function getModel($request, $var_name, $new_model)
{
if (empty($request->session()->get($var_name))) {
return $new_model;
}
return $request->session()->get($var_name);
}
$applicant = $this->getModel($request, 'applicant', new Applicant);
$spouse = $this->getModel($request, 'spouse', new Spouse);
The code works, but my concern lies in whether or not this is bad practice. Will instantiating these classes without using them waste memory?
Why are you overthinking for that? If it can affect your system if you remove it, then don't think too much about it. You can learn everyday and can find solution in a easiest way. Don't overthinking it will exhaust you and affect your productivity. As long as is it readable and not super slow then its okay.
I have a snippet that resembles the following:
while (true) {
$myObject = new Class();
$myOtherObject = $myObject->getSomeOtherObj();
...
$myArray = [1, 2, 3];
...
sleep(1); //Sleep at the end, to save CPU.
}
This snippet should run as a daemon service, but I'm having a lot of trouble making this work.
The issue: each iteration increases the process memory usage. As if at each new iteration a new $myObject is being instantiated, but the previous one remains allocated in memory, and such.
I have tried:
to unset all variables at the end of the loop (right before the sleep()).
Setting all variables to null.
encapsulating them in a separate function (while (true) { doThis(); })
manually calling gc_collect_cycles()
None of those worked to decrease memory usage.
I have no idea how to force all memory to be released.
After much research on the topic, I am finally convinced that there are no ways to manually force the memory to be released or to force object destruction.
However, something that has helped me lower the memory usage (absolutely preventing infinite memory stacking was not possible) was to realize that there are no loop scopes in PHP and that the Garbage Collection happens when switching scopes.
In C# or Java, a variable created within a while (...) {} is only accessible from within the loop. This is not the norm for PHP. A $myObject created from within a while instruction is accessible throughout your entire application.
This means the provided snippet would be better presented as:
while (true) {
myFunc();
}
function myFunc()
{
$myObject = new Class();
$myOtherObject = $myObject->getSomeOtherObj();
...
$myArray = [1, 2, 3];
...
sleep(1); //Sleep at the end, to save CPU.
}
Encapsulating the logic in a function forces the scope to change, which means the Garbage Collector will be called at each iteration. This has not solved my problem, but it has lowered my RAM usage somewhat.
What I have learned from this experience is that PHP is probably not suited to this specific project requirement. I'd need more control over memory, and PHP doesn't provide any kind of control over created/destroyed objects. Some native functions do not release memory properly (specially those that do I/O, database access and memcached).
I'm compiling my previous comments in an answer here. This doesn't explain exactly how you can free allocated memory, but it will guide you through a way to discover what in your application is causing that. With that, you can work on optimizing your code.
Finding memory usage bottlenecks is usually a challenging task. You can start by looking at your I/O-related calls, like database queries, file access, or even networking. Beyond increasing the execution time, sometimes these operations can allocate some amount of memory.
If you're already removing from memory the resources returned by I/O operations and no considerable decrease in allocated memory is noticed, the next step might be profiling your application using a tool like Blackfire (https://blackfire.io/).
Blackfire will give you a detailed view of each function call and its statistics on memory, CPU, and execution time. With that data, it's possible to check which operations are allocating excessive memory. You can find this info when you land your mouse pointer over the memory bar inside the call details, like this:
It is very likely (provided with the information given) that there are still references to the created object which prevent the garbage collector from removing the object from memory. Since it is basically counting references, therefor making sure that no reference is being stored, by either making copies of values or unsetting them carefully this can be avoided.
Normally it is easier when using while(true) constructs to not create objects precisely for this reason and make it as self contained as possible just to make sure that no memory leaks can actually happen.
I know this answer is not very helpfull in a direct manner (and I do not have enough rep to comment on the question) but it might get you on the right track.
The problem is that you are in an infinite loop, with no end at the request. The garbage collector of PHP is designed to be dealt with at the end of the request, and it not accessible to the user otherwise. PHP is designed to be called and discarded, not to be kept alive indefinately. Hence it is not fixable. So, what I would suggest is to create a chron job that restarts the php loop at regular intervals thus ending the request and freeing up the memory. See this document for details.
My best guess (due to lack of knowledge of the internals of the Classes involved) is that either the classes assign other Objects as their properties (or perhaps they have self-references) or that the array used (since the code sample resembles the real case) has a reference to itself, which would explain the memory leak if the size of the array is significant.
If it is of any help, please check out the reference counting fundamentals from php.net:
http://php.net/manual/en/features.gc.refcounting-basics.php
As both #m1lt0n and #MartPluijmaekers has mention above, this might be an issue related to object references.
We don't know what is inside your Class() class and getSomeOtherObj() method, so I can't say anything for sure, however below snippet might be able to help you figure out if that is the case or not.
/* Here is your Class */
class Class {
public function __construct () {
$this->child = new AnotherClass( $this );
}
/* This is what you have to have ... */
protected function __destruct () {
unset( $this->child );
}
}
/* Here is the other class */
class AnotherClass {
public function __construct ( $parent ) {
$this->parent = $parent;
}
}
/* Infinite Loop */
while ( true ) {
$myObject = new Class();
$myOtherObject = $myObject->getSomeOtherObj();
$myArray = [1, 2, 3];
/* manually destroying the object */
$myObject->__destruct();
unset( $myObject );
/* rest of free-ing goes here ... */
sleep(1); //Sleep at the end, to save CPU.
}
The snippet should be pretty much self-explanatory.
You can see two simplified snippets below that don't vary in their outcome.
Pattern 1, objects from scratch:
foreach ($recipients as $recipient) {
$message = new Message();
$message->setBody("This is the body of the message.");
$message->setRecipient($recipient);
$transport->sendMessage($message);
$persister->saveToDatabase($message); // Updated line
unset($message);
}
Pattern 2, cloning a prototype object:
$prototype = new Message();
$prototype->setBody("This is the body of the message.");
foreach ($recipients as $recipient) {
$message = clone $prototype;
$message->setRecipient($recipient);
$transport->sendMessage($message);
$persister->saveToDatabase($message); // Updated line
unset($message);
}
unset($prototype);
Does the object cloning (pattern 2) provide performance improvements over creating objects from scratch (pattern 1) in terms of memory usage, garbage collection and/or CPU cycles? Consider also high number of fixed properties (that do not change between the instances) and high number of loops.
Update: I need different object instances in each loop. I added saveToDatabase call to the examples to resemble that, let it for example give an ID to the message. ;)
Looks like someone has helped you with your code, but for the benefit of others visiting the question, here's the answer to what is asked in the title:
Usually. The new keyword causes the __construct() magic method to run; the clone keyword causes the __clone() magic method to run.
The point of the Prototype Pattern is to avoid rerunning an expensive constructor repeatedly, especially when the end result (in terms of the internal state of the objects) is the same each time.
The Prototype Pattern is only normally used where there is a significant performance problem that needs to be addressed, not just if you need a lot of objects.
In your case, object cloning is not necessary.
Look at this:
$message = new Message();
$message->setBody("This is the body of the message.");
foreach ($recipients as $recipient) {
$message->setRecipient($recipient);
$transport->sendMessage($message);
}
This should use the least memory. And you can't destroy an object. Let the GC do it for you.
I'm not sure if manually unsetting is necessary..
Best way to destroy PHP object?
What's better at freeing memory with PHP: unset() or $var = null
In terms of memory usage cloning should be the same as new object since every property is copied. But cloning is slightly faster. Look at this benchmark.
There is a class like this in codeigniter framework ( I edited it to be more clear, full function is here http://pastebin.com/K33amh7r):
function &load_class($class, $directory = 'libraries', $prefix = 'CI_')
{
static $_classes = array();
// Does the class exist? If so, we're done...
if (isset($_classes[$class]))
{
return $_classes[$class];
}
is_loaded($class);
$_classes[$class] = new $name();
return $_classes[$class];
}
So, first time when class is loaded ( passed to this function), it will be saved to this static variable. Next time when the same class is loaded, this function checks if class exists already ( if it's already assigned to static, cached, I'm not sure how in memory is this stored) and if it exists, it's loaded ( NOT *instantiated* again )
As far as I can see, the only purpose is to save time or memory and not instantiate the same class twice.
My question here is: Does really instantiating a class can take up memory or consume loading time so it has to be cached like this?
CodeIgniter is is geared for rapid prototyping, and is really not a good example of enterprise patterns in almost any cases. This behavior is related to their design choice of the relationship the "controller" has to almost all other objects; namely that there is exactly one of almost anything (only one instance of controller, only one instance of each library, etc). This design choice is more for rapid development (justified by the developer "not having to keep track of as much" or some such...).
Basically, there is memory saved by not instantiating an object (as much memory as it takes to store the object's instance variables) and if the object's constructor tries to do a fair bit of work, you can save time, too.
However, the appropriateness of the single-instance imperative is clearly not universal; sometimes you really do want a new instance. When you can justify this, choose a better framework.
The resources and time used in instantiating a class are usually negligible. The main reason I usually see singleton classes used is to maintain data integrity. For example, if you have a class that represents data in a database, creating multiple objects for it can cause the data to become out of sync. If one object changes and commits data to the DB, the other objects could have old data.
Its rather a simple concept, utilizing singleton-pattern it makes sure that one class is instantiated only once during an application's execution cycle.
This sort of concept apply for libraries more. Lets see a basic example:
class Authenticate {
public function login($username, $password) {
....
}
public function logout() {
}
}
Now, through a execution of a page, there is hardly any case that the object of the above class, needs to be created more than once. The main thing to understand is Utilization of resources
And YES, instantiating same class over and over again will without a doubt add up in the memory, although it might be negligible like in the example I have shown, but it does affect.