1

instruments screen capture

I'm having difficulty understanding why this is consuming memory.

I have tried;

  1. Allowing more time for ARC to clean up
  2. Creating a __weak copy of globals to pass
  3. Looked at using __bridge or __bridge_transfer but I don't believe this is appropriate.
  4. Making globals public and referencing it directly (works, but impractical)

This iOS Objective c thread is translated via j2objc 0.9.3 from a Java App.

@implementation Comms_StatusThread

- (void)run {
while (true) {

    // Consumes memeory at aproximately 100k per 5 min
    [S globals];

    @try {
        [JavaLangThread sleepWithLong:10];
    }
    @catch (JavaLangInterruptedException *e) {
    }
}

This translated static singleton stores "globals" to be accessed from anywhere in the app (the real code stores many more classes & callbacks).

@implementation S

Globals * S_globals__ = nil;

+ (Globals *)globals {
    {
        if (S_globals__ == nil) S_globals__ = [[Globals alloc] init];
        return S_globals__;
    }
}

@end

Any help appreciated. I'm new to objective-c and ARC. I've read a fair amount on ARC, but still don't understand the cause of this memory consumption.


Thanks to Student T I tried the following.

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
    [NSTimer scheduledTimerWithTimeInterval:0.01 target:self selector:@selector(test:) userInfo:nil repeats:YES];
    return YES;
}

-(void) test: (NSObject*) o {
    [S comms];
    [S globals];
}

This does not consume memory and I was planning to do this however tball's new answer (use j2objc @AutoreleasePool) is clearly the best option, so I'll start there.

Thank you so much for all your answers!

Lorne K
  • 109
  • 7
  • 1
    "Allowing more time for ARC to clean up" I dont think ARC works the way you think it does. ARC simply inserts the release for you at compile time. it isn't like Java where there is a garbage collector that scans though and deallocates things. – Brad Allred Aug 29 '14 at 05:33

3 Answers3

2

S_globals is not static; it quite clearly lacks the static modifier keyword. So you're creating a new one every time time you loop, which is happening continuously.

Frankly, that code is horrid. Do everyone a favor and don't use a Java translator.

Jon Shier
  • 12,200
  • 3
  • 35
  • 37
  • Not quite. If it's using ARC, which I assume it's (because he said it), it wouldn't be a problem. – ABCD Aug 29 '14 at 04:49
  • Also, marking it static really just means the variable can't be accessed outside of the source code. – ABCD Aug 29 '14 at 04:49
  • 3
    It won't leak in the long term, but because the code is in a tight, infinite loop, ARC doesn't get a chance to do its thing. JShier is right, the code is horrid and the OP will spend more time figuring out why things don't work than it would take to write it properly in ObjC – Paulw11 Aug 29 '14 at 04:50
  • 2
    ARC is likely waiting for the loop to end to release the memory. Since it never ends the memory is never released. – Jon Shier Aug 29 '14 at 04:52
  • And that's not what static means, Student T. – Jon Shier Aug 29 '14 at 04:54
  • Sorry I did not show the .h file which indicates that globals is static. I did test for 'creating a new one every time' and that was not the problem. – Lorne K Aug 29 '14 at 15:06
1

If it's necessary to write a loop that continually creates objects, memory will grow regardless whether ARC is used or not. The 'A' in ARC is "automatic", as its purpose is to automate use of retain/release/autorelease methods. These methods don't go away with ARC, they just are handled by the compiler instead of the developer.

What j2objc added for long-lived objects (like thread pool executors, unit test runners and benchmarks) is the AutoreleasePool annotation, which (no surprise) generates "@autoreleasepool { ... }". This annotation can be used on methods and for loop variable declarations (not elsewhere in Java 7 because annotations require declarations, but we'll be able to improve support with Java 8). Since creating and draining an autorelease pool would hurt performance, pick a reasonable sized work "chunk", such as the following example:

public void run() { while (true) { for (@AutoreleasePool int i = 0; i < 10000; i++) { S.globals(); } } }

You should be able to run this as long as your heart desires without running out of memory.

tball
  • 1,984
  • 11
  • 21
  • @Lorne K: is j2objc being used in the classroom? If so, we'd love to hear your experiences at j2objc-discuss@googlegroups.com. – tball Aug 29 '14 at 17:28
  • Wow - this is fantastic. I clearly should have searched the j2objc site more thoroughly before posting. This is not for school - we are an 18 month old start up. Primarily hardware, but I have about 14000 lines of Android code which has translated very nicely to Mac & iOS. Really a life saver as the hardware is complex - multiple protocols, types of encryption, connection modes, data types, etc. If a feature works in Android, the iOS/Mac translations always work perfectly. We have never had a problem (just the memory issue). Also nice to offload this stuff so I can do Firmware work :) – Lorne K Aug 30 '14 at 03:57
0

Assuming you're compiling using ARC, because if you aren't, the whole conversion is meaningless. Your experiment wouldn't work because you put the code in a infinite loop. You tried to sleep the thread but it wouldn't work because you were also stopping the main running thread at the same time. You need to give the main run-loop a chance to run in the 10s duration.

ABCD
  • 7,914
  • 9
  • 54
  • 90