Open jmspereira opened 3 days ago
Does anything change if you add
import gc
gc.collect()
after open_pillow_image()
?
https://github.com/python-pillow/Pillow/issues/7935#issuecomment-2031804237
Pillow's memory allocator doesn't necessarily release the memory in the pool back as soon as an image is destroyed, as it uses that memory pool for future allocations. See Storage.c (https://github.com/python-pillow/Pillow/blob/main/src/libImaging/Storage.c#L310) for the implementation.
@Yay295, calling the garbage collector explicitly does not make any difference.
@radarhere according to the documentation:
"There is now a memory pool to contain a supply of recently freed blocks, which can then be reused without going back to the OS for a fresh allocation. This caching of free blocks is currently disabled by default (...)" (https://pillow.readthedocs.io/en/stable/reference/block_allocator.html)
It appears that the caching of free blocks should be disabled by default, and tweaking with the PILLOW_BLOCKS_MAX as mentioned in the issue that you reference does not make any difference.
I see, "caching of free blocks" refers to https://github.com/python-pillow/Pillow/blob/5bff2f3b2894ec6923c590d0c37b18177d0634bd/src/libImaging/Storage.c#L315-L338
By default, the following is used instead. https://github.com/python-pillow/Pillow/blob/5bff2f3b2894ec6923c590d0c37b18177d0634bd/src/libImaging/Storage.c#L339-L349
Testing further, I think the issue doesn't occur only when loading the array, but rather when saving.
If I suggest that calling JpegImagePlugin directly improves the situation, do you agree?
from PIL import JpegImagePlugin
with BytesIO() as output, Image.fromarray(random_image) as pillow_image:
pillow_image.encoderinfo = {}
JpegImagePlugin._save(pillow_image, output, "filename")
Hum, It doesn't seem to make any difference
Do you agree that saving is the problem? As in, I think this code should be fine.
with BytesIO() as output, Image.fromarray(random_image) as pillow_image:
pass
Hum, I do not think so. If I run this:
import time
from io import BytesIO
import numpy as np
from PIL import Image
def open_pillow_image():
random_image = (np.random.rand(720, 1280, 3) * 255).astype(np.uint8)
with BytesIO() as output, Image.fromarray(random_image) as pillow_image:
pass
def main():
print("before")
time.sleep(10)
open_pillow_image()
print("after")
time.sleep(1000)
if __name__ == '__main__':
main()
The memory used by the script is larger after opening the image.
Just to be sure, if you remove Pillow, does the problem go away?
import time
from io import BytesIO
import numpy as np
def open_pillow_image():
random_image = (np.random.rand(720, 1280, 3) * 255).astype(np.uint8)
with BytesIO() as output:
pass
def main():
print("before")
time.sleep(10)
open_pillow_image()
print("after")
time.sleep(1000)
if __name__ == '__main__':
main()
Yes, the problem does not exist without pillow.
What did you do?
Hey everyone, I have an application that uses pillow to encode numpy arrays as jpegs, however I am seeing a strange behavior regarding the memory usage of that application.
What did you expect to happen?
All allocated memory be freed.
What actually happened?
There is memory that is not freeded.
What are your OS, Python and Pillow versions?
Code that reproduces the problem: