I have an instance of UIImage with an image with size of 200KB, then I create 5 instances of UIImageView that reference to same this UIImage.
I wonder how much memory allocated in this case - only 200KB (of one UIImage instance) or 1MB (for 5 cloned UIImage instances)? In the case of wasting memory occured, is there effective way to solve it?
A couple of thoughts:
UIImage is a reference type, so when you reference the same image five times, you generally will have one image object in memory. It depends a little upon how you do this. For example, if you use UIImage(data:) each time, or something like that, it's possible to instantiate a new object each time, but if you instantiate only one UIImage and then proceed to use if five times, then you won't see duplicative memory consumption taking place.
As an aside:
You say the image has a size of 200kb. Is that the size of the original asset, or have you figured out that this is how much memory it will take at run time?
The reason I ask is that JPG and PNG files are generally compressed, but when you use it in an image view, it will be uncompressed. The amount of memory that an image takes has little to do with the file size of the original asset, but rather corresponds to the dimensions (in pixels) of the image. So a random PNG that is 676 kb that is 2560 x 1440 pixels may actually require 14mb of memory (four bytes per pixel).
Note, this memory consumption corresponds to the dimension of the image in question, not the dimensions of the image view to which you added it. If you're concerned about memory usage and if the image dimensions exceed the size of the image view (times the device scale), then you might want to consider resizing the image.
In the future, you can answer these questions empirically using Instruments. For example, in the following timeline, at the green signpost, I loaded a UIImage with the 676kb asset with modest memory impact, I set the image view image to use this asset at the purple signpost with a significant memory impact as it uncompressed this 2560 x 1440 px image, and I loaded five more image views with the same image at the orange signpost with negligible further memory impact.
Related
The login view in our app uses large background images. To try and save on memory/app size I resized and compressed these images, which reduced their filesize significantly (less than 1mb, down from several mb).
When monitoring my apps memory usage (XCode debugger) there is a clear spike when a modified image is displayed (around 30-40mb). I'd accepted this as normal and simply made sure to release the image asap to limit memory usage.
I've recently started replacing a couple of the images and wanted to preview the new ones before resizing/compressing them. I noticed that these images (one of which is 11mb on disk and 4640x3472 pixels) has no visible effect on app memory usage whatsoever, increasing 'Other Processes' instead (by around 20-30mb).
Can anyone explain what's happening here? I want to confirm it is advisable to continue resizing/compressing the images.
Note that I'm loading the images using UIImage(contentsOfFile:) and I resized/compressed the images using GIMP. The new images have been taken straight from Flickr and unmodified.
Cheers.
The in-memory size of the image (as a UIImage) is different to the compressed on-disk size (your JPEG)
The UIImage takes 4 bytes (RGBA) per pixel x height x with - so for a 4640 x 3472 image, you're looking at 64,440,320 bytes - quite different to the 11MB on disk
I am working on application which include native SQLite database in which I am storing and retrieving images and showing into My application.
Now my problem is Like I am storing lots of images in directory and its path storing into database. So when I retrieve that path from database and load image into application, Memory Increases upto 10-20 Mb per image.
I also tried to store image data into database but same issue, Memory increase 10-20 Mb per image.
Please what should I do for this memory issue ?
Help me with it
Images, when used in the app, may require considerably more memory than the size of the asset in persistent storage might otherwise suggest. Assets are frequently compressed (e.g. JPG or PNG), but when you use the image, they're uncompressed, often requiring 4 bytes per pixel (one byte for red, green, blue, and alpha, respectively). So, for example, a iPhone 7+ full-screen retina image can require 14mb when you use the image. So, the memory-efficient technique is to employ lazy loading, not creating the UIImage objects until you absolutely need them. And, as Jerry suggested, because the amount of memory is determined by the size of the image and not the imageview in which you use the image, if your images have dimensions greater than required by the UIImageView in which you use them (i.e. width and height of the imageview times the "scale" of the device), you may want to resize the image accordingly.
It may be the case that the images you are trying to display are much larger than they need to be on the screen. Try loading the images into memory, creating a version with the size you need, and then using that. Of course, you could implement some caching so you don't have to keep resizing the same images. But then when the original image goes out of scope, its memory will be released. If you always need your images at the same size, try resizing them before storing them in the database, but if you want to support multiple sizes (for different devices, maybe), then store the images at the largest size you need and resize for the others.
I'm making an app that uses a large amount of UIImageView's and I am wanting to find out the size of each of the UIImageViews's to try and take a look at the app's memory usage in more detail.
I have played around with Instruments and I can see the amount of memory being alloced by the app but I am wanting a more in depth look at what objects use what memory. For example something like:
UIImageViewOne 10Mb
UIImageViewTwo 8Mb
UIImageViewThree 9Mb
UIImageViewFour 3Mb
Is this possible with Instruments or is there an easier way to view this information?
If you want to determine the amount of memory that will be used by an image, you can look at the cgImage property (which returns a CGImageRef) and then supply that as the parameter to bytesPerRow and multiply that by the height of the image. So, would look like the following (in Objective-C implementation would use CGImageGetBytesPerRow and CGImageGetHeight):
guard let cgImage = image.cgImage else { return }
let bytes = cgImage.bytesPerRow * cgImage.height
This, calculates the size of the pixel buffer used by the image when it is uncompressed and used within the app. Frequently this total is equal to 4 times the width times the height (both measured in pixels). (This is one byte for each of the four channels, red, green, blue, and alpha.) This isn't all of the memory associated with the image, but it accounts for the vast majority of it.
When I ran the above code on my 2,880 × 1,800 pixel image, it reported that it took 20,736,000 bytes (i.e. 19.78 mb).
As you know, if you want to see this in instruments, you can use the allocations tool and then drag within a specific range in the graph and you'll see the objects allocated, but not released, within that time period:
You'll see that for that selected range, the largest allocation was 19.78 mb of a ImageIO_PNG_Data (or if you used a JPG, you'd see a ImageIO_jpeg_Data).
Unfortunately, tracking these ImageIO allocations back to our code is a little more complicated than with most memory allocations, because of the sleight of hand that iOS does during the uncompression. But you can see how this ImageIO allocation correlates to the calculated pixel buffer size.
(See revision history for rendition for earlier versions of Swift.)
This image (http://imgur.com/TyPtrxy) will not load in the simulator, although when I scale it to half the size it loads just fine. When trying to load the full image I just get a black box where it should be.
Yes, there is a maximum image size (number of pixels). The limit depends on the hardware in part, but it is generally in the range of 5 to 10 million pixels. This limit is related to limitations on the maximum sizes of textures that can be sent to the graphics card; therefore, it only applies to images that are drawn.
From the documentation:
You should avoid creating UIImage objects that are greater than 1024 x 1024 in size. Besides the large amount of memory such an image would consume, you may run into problems when using the image as a texture in OpenGL ES or when drawing the image to a view or layer. This size restriction does not apply if you are performing code-based manipulations, such as resizing an image larger than 1024 x 1024 pixels by drawing it to a bitmap-backed graphics context. In fact, you may need to resize an image in this manner (or break it into several smaller images) in order to draw it to one of your views.
It might be that you are hitting some limits on the maximum size of the CALayer (which in turn is dependent on the maximum OpenGL texture size supported by the hardware) that is backing the view. If you're exceeding the maximum size, a message like CoreAnimation: surface <size> is too large will be logged. It's also possible that the decompressed image may be too large to fit in memory. You should use CATiledLayer to display content of that size to ensure that it stays within the resource constraints of the device.
Just to expand a bit on the other answers.
The UIImage documentation (as of iOS 10) no longer seems to mention size limitations, although if you use UIImageView with images whose dimensions are larger than the maximum texture size* supported by the device you happen to be using you do get very large memory consumption at render time.
(The memory consumption I see in Instruments seems to indicate that the entire image is put into a 32 bits per pixel buffer when the CA::Layer is rendered.)
If your app doesn't get kill by the OS due to memory usage, the UIImageView does still end up displaying the image though.
Given this, you'll still need strategies to deal with very large images.
* You can check the maximum texture size using something like glGetIntegerv(GL_MAX_TEXTURE_SIZE, &maxTextureSize);. Just make sure you've set the EAGLContext current context to be something non-nil before querying OpenGL, otherwise you'll get zero.
I have the following issue:
I have a primary view object (that inherits from UIView) that displays a grid of 16 squares (each is a class I created that inherits from UIImageView), in a 4x4 layout.
Each of these 16 squares is 160x160, and contains an image (a different image for each square) that is no bigger than 30kb. The image, however, is 500x500 (because it is used elsewhere in the program, in its full size), so it gets resized in the "square" class to 160x160, by the setFrame method.
By looking at the memory management feature of Xcode when the app is running, I've noticed a few things:
each of these squares, when added to the primary view object, increase the memory usage of the app by 1MB. This doesn't happen at instantiation, but only when they are added by [self addSubview:square] at the primary view object.
if I use the same image for all the squares, the memory increase is
minimal. If I initialize the square objects without any images, then
the increase is basically zero.
the same app, when running in the simulator, uses 1/6 of the memory
it does on an actual device.
The whole point here is: why is each of the squares using up 1MB of memory when loading a 30kb image? Is there a way to reduce this? I've tried creating the images in a number of different ways: [UIImage imageNamed:img], [UIImage imageWithContentsFromFile:path], [UIImage imageWithData:imgData scale:scale], as well as not resizing the frame.
When you use a 500x500 image in a smaller UIImageView, it's still loading the larger image into memory. You can solve this by resizing the UIImage, itself (not just adjusting the frame of the UIImageView), making a 160x160 image, and use that image in your view. See this answer for some code to resize the image, which can then be invoked as follows:
UIImage *smallImage = [image scaleImageToSizeAspectFill:CGSizeMake(160, 160)];
You might even want to save the resized image, so you're not constantly encumbering yourself with the computational overhead of creating the smaller images every time, e.g.:
NSData *data = UIImagePNGRepresentation(smallImage);
[data writeToFile:path atomically:YES];
You can then load that PNG file corresponding to your small image in future invocations of the view.
In answer to your question why it takes up so much memory, it's because while the image is probably stored as a compressed JPG or PNG in persistent storage, I suspect in memory it's held as an uncompressed bitmap. There are many internal formats, but a common one is a 32-bit format with 8 bits each for red, green, blue, and alpha. Regardless of the specifics, you can quickly see how a 500 x 500 pixel representation, with 4 bytes per pixel could translate to a 1 mb of memory. But a 160 x 160 image should be roughly one tenth the size.