Get amount of memory used by app in iOS - iphone

I'm working on an upload app that splits files before upload. It splits the files to prevent being closed by iOS for using too much memory as some of the files can be rather large. It would be great if I could, instead of setting the max "chunk" size, set the max memory usage and determine the size using that.
Something like this
#define MAX_MEM_USAGE 20000000 //20MB
#define MIN_CHUNK_SIZE 5000 //5KB
-(void)uploadAsset:(ALAsset*)asset
{
long totalBytesRead = 0;
ALAssetRepresentation *representation = [asset defaultRepresentation];
while(totalBytesRead < [representation size])
{
long chunkSize = MAX_MEM_USAGE - [self getCurrentMemUsage];
chunkSize = min([representation size] - totalBytesRead,max(chunkSize,MIN_CHUNK_SIZE));//if I can't get 5KB without getting killed then I'm going to get killed
uint8_t *buffer = malloc(chunkSize);
//read file chunk in here, adding the result to totalBytesRead
//upload chunk here
}
}
Is essentially what I'm going for. I can't seem to find a way to get the current memory usage of my app specifically. I don't really care about the amount of system memory left.
The only way I've been able to think of is one I don't like much. Grab the amount of system memory on the first line of main in my app, then store it in a static variable in a globals class then the getCurrentMemUsage would go something like this
-(long)getCurrentMemUsage
{
long sysUsage = [self getSystemMemoryUsed];
return sysUsage - [Globals origSysUsage];
}
This has some serious drawbacks. The most obvious one to me is that another app might get killed in the middle of my upload, which could drop sysUsage lower than origSysUsage resulting in a negative number even if my app is using 10MB of memory which could result in my app using 40MB for a request rather than the maximum which is 20MB. I could always set it up so it clamps the value between MIN_CHUNK_SIZE and MAX_MEM_USAGE, but that would just be a workaround instead of an actual solution.
If there are any suggestions as to getting the amount of memory used by an app or even different methods for managing a dynamic chunk size I would appreciate either.

Now, as with any virtual memory operating system, getting the "memory used" is not very well defined and is notoriously difficult to define and calculate.
Fortunately, thanks to the virtual memory manager, your problem can be solved quite easily: the mmap() C function. Basically, it allows your app to virtually load the file into memory, treating it as if it were in RAM, but it is actually swapped in from storage as it is accessed, and swapped out when iOS is low on memory.
This function is really easy to use in iOS with the Cocoa APIs for it:
- (void) uploadMyFile:(NSString*)fileName {
NSData* fileData = [NSData dataWithContentsOfMappedFile:fileName];
// Work with the data as with any NSData* object. The iOS kernel
// will take care of loading the file as needed.
}

Related

What will be the size(memory)of NSMutableArray?

I am parsing json data,and i store them into an NSMutableArray.
For each data,parameters are as the following format,
{
"id":"6",
"username":"biju",
"password":"biju123",
"usertype":"2",
"first_name":"biju",
"last_name":null,
"email":"b#b.com",
"blocked":"N",
"created":"2012-11-02 16:03:19",
"image":"http:\/\/192.168.1.254\/eatthos\/assets\/upload\/users\/1351852399_thumb.jpg","thumb_image":"1351852399_thumb.jpg",
"menu_image":"0",
"thumb_menu_image":"0",
"city":"njdfh",
"favorite_food":"kafh",
"favorite_restaurant":"kafdhj",
"phone_number":"0",
"description":"0",
"token":"Dwx0DG",
"fb_unique_id":null,
"account_type":"normal",
"twitter_id":null,
"followers":"5",
"follow":"N"
}
i am parsing about 100K of data,what will be the size(memory) of that array ?
Will it be a memory issue if i use it for an iphone app ?
You can try to find out size of your array with the following piece of code :
size_t total;
id obj;
for (obj in array)
{
total += class_getInstanceSize([obj class]);
}
NSLog(#"Array size : %ld",total); //This will return you size in bytes
you need to do : #import <objc/runtime.h>
The array itself won't take that much space -- it's just a bunch of pointers. An array with tens of thousands of items can still only be a few dozen kilobytes. The space taken by the objects that the array contains is likely to be much more significant. But that is something only you can see -- there is no "size of an object in an array." It's like asking "How big is a ball?" It is possible that your data's space needs are problematic, and it's equally possible that there's no problem at all. As with many programming questions, I think the best answer is to try and see for yourself.
Why can not you download 100 records each time like an apps then are in app store
So, That the app will load quicker that before
If u want to see the flow of the memory you can go through this link http://mobileorchard.com/find-iphone-memory-leaks-a-leaks-tool-tutorial/

NSCache is not evicting data

NSCache is a rarely used tool which actually looks quite useful. I created a simple experiment to see how it works and it looks like it does not auto-evict data in low memory situations (or I am doing something wrong!)
- (void)viewDidLoad
{
_testCache = [[NSCache alloc] init];
// Allocate 600 MB of zeros and save to NSCache
NSMutableData* largeData = [[NSMutableData alloc] init];
[largeData setLength:1024 * 1024 * 600];
[_testCache setObject:largeData forKey:#"original_Data"];
}
- (IBAction)buttonWasTapped:(id)sender {
// Allocate & save to cache 300 MB each time the button is pressed
NSMutableData* largeData = [[NSMutableData alloc] init];
[largeData setLength:1024 * 1024 * 300];
static int count = 2;
NSString* key = [NSString stringWithFormat:#"test_data_%d", count++];
[_testCache setObject:largeData forKey:key];
NSMutableData* dataRecoveredFromCache = [_testCache objectForKey:#"original_Data"];
if (dataRecoveredFromCache) {
NSLog(#"Original data is ok");
} else {
NSLog(#"Original data is missing (purged from cache)");
}
}
So I ran the app in the simulator, and taped the button a few times however no items were evicted... The app eventually crashed:
2012-07-17 14:19:36.877 NSCacheTest[15302:f803] Data is ok
2012-07-17 14:19:37.365 NSCacheTest[15302:f803] Data is ok
2012-07-17 14:19:37.861 NSCacheTest[15302:f803] Data is ok
2012-07-17 14:19:38.341 NSCacheTest[15302:f803] Data is ok
2012-07-17 14:19:38.821 NSCacheTest[15302:f803] Data is ok
NSCacheTest(15302,0xac0942c0) malloc: *** mmap(size=393216000) failed (error code=12)
*** error: can't allocate region
From the doc (Emphasis mine): The NSCache class incorporates various auto-removal policies, which ensure that it does not use too much of the system’s memory. The system automatically carries out these policies if memory is needed by other applications. When invoked, these policies remove some items from the cache, minimizing its memory footprint.
Apple does not state that the memory will be freed on memory warning - in my experience, the cache is most often purged when the app goes to background or when you add more large elements.
Here's quoted docs ...
The NSCache class incorporates various auto-removal policies, which
ensure that it does not use too much of the system’s memory. The
system automatically carries out these policies if memory is needed by
other applications. When invoked, these policies remove some items
from the cache, minimizing its memory footprint.
... as you can see it states that it removes some items, not all items. It depends on NSCache internal policies, available memory, device status, etc. You shouldn't worry about these policies.
You can control them with countLimit, totalCostLimit properties and you can add object with cost, look at setObject:forKey:cost:.
Also you can evict objects by yourself. Add NSDiscardableContent protocol implementation to your objects and setEvictsObjectsWithDiscardedContent: to YES.
I am using that class too. Note that the documentation states the NSCache is tied into the OS and probably has access to memory information deep within the OS. The memory warning is just that - it just sends a memory warning to the appDelegate/viewControllers.
If you really want to test your code out, you will probably need a test mode where you start mallocing lots of memory (creating a huge leak so to speak). You might need parcel this out in chunks during each main runloop, so the OS has the opportunity to see he memory going down (I have an app that chew ups lots of memory, and it does it so fast on the 3GS it just gets killed never having got a memory warning.

Calling ALAssetReprsentation's metadata method several hundred times will fail

I am writing a tiny iPhone app to retrieve metadata, such as EXIF info, for all photos stored in the iPhone, and ran into a weird issue when calling the Assets Library Framework API. Basically, if I am calling ALAssetReprsentation's metadata method (http://developer.apple.com/library/ios/documentation/AssetsLibrary/Reference/ALAssetRepresentation_Class/Reference/Reference.html#//apple_ref/occ/instm/ALAssetRepresentation/metadata) for several hundred times (even for the same ALAssetReprsentation object), the API will report an error and return null instead of photo's metadata.
Here is the code to reproduce this issue:
ALAsset *photo = ... // fetch a photo asset via Assets Library Framework
int i = 0;
ALAssetRepresentation *representation = [photo defaultRepresentation];
NSDictionary *metadata;
while (i<600) {
i++;
metadata = [representation metadata];
NSLog(#"photo %d indexed %#", i, metadata);
}
Here is the output for the code above. In the beginning of the output, everything is okay, but after 500+ times, the metadata API will report error like "ImageIO: CGImageSourceCreateWithData data parameter is nil".
...
2011-12-29 21:46:17.106 MyApp[685:707] photo 578 indexed {
ColorModel = RGB;
DPIHeight = 72;
DPIWidth = 72;
...
}
...
ImageIO: <ERROR> CGImageSourceCreateWithData data parameter is nil
2011-12-29 21:46:17.151 MyApp[685:707] photo 579 indexed (null)
ImageIO: <ERROR> CGImageSourceCreateWithData data parameter is nil
2011-12-29 21:46:17.177 MyApp[685:707] photo 580 indexed (null)
I am testing in an iPhone 3GS with iOS 5.0.1. And I am developing with Xcode 4.2 with ARC (automatical reference counting) enabled. And I can only reproduce this issue when deploying the app to the iPhone 3GS device, but cannot reproduce this issue when using iOS simulator with the same code (at least I don't reproduce this issue after calling the API over 1800 times in iOS simulator).
Any help is appreciated. Thanks.
It is possible that you are running out of memory. The method [representation metadata] returns an autoreleased object and possibly creates more autoreleased objects when it executes. All these instances are added to the autorelease pool, waiting to be finally released (and their memory freed) when the ARP gets the chance to drain itself.
The problem is that this won't happen until your code returns control to the run loop. So for the duration of your loop, at least 600 large dictionaries (and possibly many more objects) end up being allocated and not deallocated. Depending on the size of these objects, memory usage can increase tremendously.
This is true whether you are using ARC or not.
To avoid this issue, try creating a fresh autorelease pool on every iteration of the loop. That way, the ARP gets drained on every iteration:
while (i<600) {
#autoreleasepool {
i++;
metadata = [representation metadata];
NSLog(#"photo %d indexed %#", i, metadata);
}
}
This is not necessarily the best solution from a performance perspective but at least it will tell you whether the problem is memory related.
PS: Your code doesn't make much sense at the moment. Why retrieve the metadata for the same asset 600 times in a row?
enter code hereMake sure you retain the ALAssetLibrary until you are finished access related assets. From Apple's docs:
The lifetimes of objects you get back from a library instance are tied
to the lifetime of the library instance.

Deal with potential memory issue in iPhone

What ways are there to deal with memory issues on the iPhone?
Is it possible to ask how much memory is available before jumping into a memory intensive section of code?
(or perhaps Apple would just say that if you have to use so much memory you are on the wrong platform?)
UIApplicationDelegate's applicationDidReceiveMemoryWarning: will let you know if you're using too much memory. If you want to check before a memory intensive operation, here's a function that gets the available free memory in bytes on iOS:
natural_t TSGetFreeSystemMemory(void) {
mach_port_t host_port = mach_host_self();
mach_msg_type_number_t host_size = sizeof(vm_statistics_data_t) / sizeof(integer_t);
vm_size_t pagesize;
vm_statistics_data_t vm_stat;
host_page_size(host_port, &pagesize);
if (host_statistics(host_port, HOST_VM_INFO, (host_info_t)&vm_stat, &host_size) != KERN_SUCCESS)
printf("failed to get host statistics");;
// natural_t mem_used = (vm_stat.active_count + vm_stat.inactive_count + vm_stat.wire_count) * pagesize;
natural_t mem_free = vm_stat.free_count * pagesize;
return mem_free;
}
Apple appears not to be telling developers because they want to change the amount of memory available in new devices and OS releases. The number went way up on a freshly booted iPhone 4 and way down under iOS 4.0 after typical use on an iPhone 3G.
One possible method is to "preflight" the memory required for successful completion of some operation (e.g. malloc, check and then free the blocks that add up to your requirement). You can malloc in small chunks using a timer spanning many milliseconds to see if you can "push" other apps out of memory. But even this method is no guarantee, as Mail or some other background app could jump in and consume memory even when your app is frontmost.
If you use less than 20MB at any point in time, then a huge percentage of games in the App store will fail before your app does (just my random opinion).
Put the following in your app delegate and it will be called when memory is starting to run low. This is the Apple way of doing things:
- (void)applicationDidReceiveMemoryWarning:(UIApplication *)application {
// Free some memory or set some flag that we are low
}

Streaming JPEGs, detect end of JPEG

I have created a java server, which takes screenshots, resizes them, and sends them over TCP/IP to my iPhone application. The application then uses NSInputStream to collect the incoming image data, create an NSMutableData instance with the byte buffer, and then create a UIImage object to display on the iPhone. Screenshare, essentially. My iPhone code to collect the image data is currently as follow:
- (void)stream:(NSStream *)theStream handleEvent:(NSStreamEvent)streamEvent{
if(streamEvent == NSStreamEventHasBytesAvailable && [iStream hasBytesAvailable]){
uint8_t buffer[1024];
while([iStream hasBytesAvailable]){
NSLog(#"New Data");
int len = [iStream read:buffer maxLength:sizeof(buffer)];
[imgdata appendBytes:buffer length:len];
fullen=fullen+len;
/*Here is where the problem lies. What should be in this
if statement in order to make it test the last byte of
the incoming buffer, to tell if it is the End of Image marker
for the end of incoming JPEG file?
*/
if(buffer[len]=='FFD9'){
UIImage *img = [[UIImage alloc] initWithData:imgdata];
NSLog(#"NUMBER OF BYTES: %u", len);
image.image = img;
}
}
}
}
My problem, as indicated by the in-code comment, is figuring out when to stop collecting data in the NSMutableData object, and use the data to create a UIImage. It seems to make sense to look for the JPEG End of File marker--End of Image (EOI) marker (FFD9)--in the incoming bytes, as the image will be ready for display when this is sent. How can I test for this? I'm either missing something about how the data is stored, or about the marker within the JPEG file, but any help in testing for this would be greatly appreciated!
James
You obviously don't want to close the stream because that would kill performance.
Since you control the client server connection, send down the # of bytes in the image before sending the image data. Better yet, send down # of bytes in the image, the image data, and an easily identified serial # at the end so you can quickly verify that the data has actually arrived.
Much easier and more efficient than actually checking for the end of file marker. Though, of course, you could also just check for that after the # of bytes have been received, too. Easy enough.
Of course, all of this is going to be grossly inefficient for screensharing style purposes in all but the unusual cases. In most cases, only a small part of the screen to be mirrored actually changes with each frame. If you try to send the whole screen with every frame, you'll quickly saturate your connection and the client side will be horribly laggy and unresponsive.
Given that this is an extremely mature market, there are tons of solutions and quite a few open source bits from which you can derive a solution to fit your needs (see VNC, for example).