ObjCInstance eat memory
@wolf71 And is your problem that you see a one time increase? Or that it continually increases? (Maybe post some sample output?). Keep in mind this way of getting memory just gets the amount allocated to the app, and I think it will not usually go down, even when you free memory - think of it as the peak heap size since launch. One-time increases each time you run a program are not that surprising, since the global clearing often leaves some garbage behind.
Try with a larger buffer, and without setting the buffer inside the processBuffer. The default would be approx. 16384. Also, delete the
cbuf.frameLength = bufsize
line, in case that is creating a strong reference.
Are you on the beta, or app store version?
App store version.
when change tap out
cbuf = ObjCInstance(buffer) to
#cbuf = ObjCInstance(buffer)
the memory eat is stop.
- Run about 30min,the pythonista will crash.
What about if you set the buffer size to 16384, and try this for your processBuffer:
def processBuffer(self,buffer,when,cmd): cbuf=ObjCInstance(buffer) del cbuf
Also, what does the faulthandler show as the reason for crash? (see @dgelessus's fault handler and uncaught exception handler module) in his pythonista_startup repo. I have found that running out of memory results in an unceremonious crash without any uncaught exceptions, but there could be other reasons as well.
When you say the memory is getting eaten, can you be more specific? i.e. how fast is it increasing?
wolf71 last edited by wolf71
- add del cbuf and change bufsize = 4096*4, but not change.still eat memory.
2.memory lost speed: about 1M Bytes/s, very fast.
Ok, this is similar to the problems I was havinig trying to process images in real time.
I think the issue is that this code in
ObjCInstancecreates a strong reference to the object in all of the ObjCInstanceMethods.
def __getattr__(self, attr): cached_method = self._cached_methods.get(attr, None) if not cached_method: if '_' in attr: # Old method call syntax cached_method = ObjCInstanceMethod(self, attr) else: # New syntax, actual method resolution is done at call time cached_method = ObjCInstanceMethodProxy(self, attr) self._cached_methods[attr] = cached_method return cached_method
I noiced if you manually run gc, the memory does go down...
dgelessus last edited by
Ah, that explains that. As a temporary hack, you could add something like
cbuf._cached_methods.clear() del cbuf
to manually break the reference cycles between the ObjCInstance and its methods. If this callback is time-sensitive like @JonB says, then this kind of hack might even be necessary, because a full
gc.collect()run can take much longer than this manual cleanup.
@JonB The method objects should probably keep a weakref to their owner, instead of a normal reference. (Making
WeakValueDictisn't an option, that would make the cache useless.)
Add this line
cbuf._cached_methods.clear() del cbuf
but the memory keep lost.just like before.
ObjCInstanceMethodProxy's do store a weakref, but ObjCinstanceMethods store a strong reference. Not sure if that was intentional. I tried fixing this in ObjCInstanceMethod, but then my block never was called, so perhaps i just missed something.
What I found digging a bit deeper:
cbuf._cached_methodshere contains a single ObjCinstanceMethodProxy... ironically the one method that was called:
retain. : The method_cache does have a strong reference back to cbuf. clearing the cache does break the ref cycle, as evidenced by checking c.CFGetRefCount before and after del cbuf ( without clearing the cache, the objc refcount never decreases when deleting cbuf, since .release doesn't get called except when then b. Doing so, for me anyway, keeps the virtual_size from the kernel task info pretty constant. I have not tried the method wolf is using.
I made a slight mod to your test code -- I download and use an mp3 file (since I don't know what Daydream.zip is), and I added the clear code which @dgelessus suggested.
I also catch a KeyboardInterrupt and stop the engine, otherwise you will crash when running the script again.
I also added a delay after starting to report memory before playing the mp3, so you can get a "baesline".
The result was a few MB increase the first time I ran the script, eventually levelling off, and no increase the second. i included a dump of e console in the gist so you can see....
It is also possible that there were some bug fixes in the beta version that prevent the problem you are seeing.
@JonB Thanks you very much.
Are you using Pythonista 3 new Beta version?
I run on my iPad Pro 9.7 and Pythonista 3 (App Store Version).
can slow down memory lost speed, total 240s audio file play,eat about 20M memory.