Should I avoid creating JSContexts in global queues?
Asked Answered
H

2

5

I've just had a crash log from a customer's device, and it's crashing here:

dispatch_async(dispatch_get_global_queue(0, 0), ^{

    JSContext *javaScriptContext = [[JSContext alloc] init];

Here's the crash log:

Thread 11 Crashed:
0   JavaScriptCore                  0x31009cd6 WTFCrash + 54
1   JavaScriptCore                  0x30e0edf6 WTF::OSAllocator::reserveAndCommit(unsigned long, WTF::OSAllocator::Usage, bool, bool, bool) + 166
2   JavaScriptCore                  0x30e0ed2a WTF::OSAllocator::reserveUncommitted(unsigned long, WTF::OSAllocator::Usage, bool, bool, bool) + 14
3   JavaScriptCore                  0x30e14736 JSC::JSStack::JSStack(JSC::VM&, unsigned long) + 74
4   JavaScriptCore                  0x30e146d2 JSC::Interpreter::Interpreter(JSC::VM&) + 22
5   JavaScriptCore                  0x30e10fb8 JSC::VM::VM(JSC::VM::VMType, JSC::HeapType) + 2516
6   JavaScriptCore                  0x30fbf48e JSC::VM::createContextGroup(JSC::HeapType) + 22
7   JavaScriptCore                  0x30fbdc86 JSContextGroupCreate + 14
8   JavaScriptCore                  0x30fd209e -[JSVirtualMachine init] + 6
9   JavaScriptCore                  0x30fbd122 -[JSContext init] + 46
10  <redacted>
11  libdispatch.dylib               0x3a776d78 _dispatch_call_block_and_release + 8
12  libdispatch.dylib               0x3a77dda0 _dispatch_root_queue_drain + 216
13  libdispatch.dylib               0x3a77df88 _dispatch_worker_thread2 + 52
14  libsystem_pthread.dylib         0x3a8b8dbc _pthread_wqthread + 296
15  libsystem_pthread.dylib         0x3a8b8c80 start_wqthread + 4

WTFCrash, indeed.

At that point, several of the other threads were busy with Javascript-related memory stuff:

Thread 10:
0   libsystem_kernel.dylib          0x3a83f970 _kernelrpc_mach_vm_deallocate_trap + 20
1   libsystem_kernel.dylib          0x3a83fc5a mach_vm_deallocate + 26
2   libsystem_kernel.dylib          0x3a83fc36 vm_deallocate + 14
3   JavaScriptCore                  0x30e18f20 JSC::BlockAllocator::releaseFreeRegions() + 64
4   JavaScriptCore                  0x30f89784 JSC::CopiedSpace::~CopiedSpace() + 20
5   JavaScriptCore                  0x30faea28 JSC::Heap::~Heap() + 336
6   JavaScriptCore                  0x30fbf434 JSC::VM::~VM() + 2600
7   JavaScriptCore                  0x30e0bb82 JSC::JSLockHolder::~JSLockHolder() + 90
8   JavaScriptCore                  0x30fbdcf8 JSContextGroupRelease + 76
9   JavaScriptCore                  0x30fd21be -[JSVirtualMachine dealloc] + 22
10  libobjc.A.dylib                 0x3a29eb06 objc_object::sidetable_release(bool) + 170
11  libobjc.A.dylib                 0x3a290002 (anonymous namespace)::AutoreleasePoolPage::pop(void*) + 354
12  libdispatch.dylib               0x3a77de08 _dispatch_root_queue_drain + 320
13  libdispatch.dylib               0x3a77df88 _dispatch_worker_thread2 + 52
14  libsystem_pthread.dylib         0x3a8b8dbc _pthread_wqthread + 296
15  libsystem_pthread.dylib         0x3a8b8c80 start_wqthread + 4

Thread 15 name:  JavaScriptCore::BlockFree
Thread 15:
0   libsystem_kernel.dylib          0x3a851f38 __psynch_cvwait + 24
1   libsystem_pthread.dylib         0x3a8ba224 _pthread_cond_wait + 536
2   libsystem_pthread.dylib         0x3a8bb040 pthread_cond_timedwait + 40
3   JavaScriptCore                  0x30e12eb8 WTF::ThreadCondition::timedWait(WTF::Mutex&, double) + 104
4   JavaScriptCore                  0x30e12ce4 JSC::BlockAllocator::blockFreeingThreadMain() + 88
5   JavaScriptCore                  0x30e103a8 WTF::wtfThreadEntryPoint(void*) + 12
6   libsystem_pthread.dylib         0x3a8bac1a _pthread_body + 138
7   libsystem_pthread.dylib         0x3a8bab8a _pthread_start + 98
8   libsystem_pthread.dylib         0x3a8b8c8c thread_start + 4

Thread 16 name:  JavaScriptCore::Marking
Thread 16:
0   libsystem_kernel.dylib          0x3a851f38 __psynch_cvwait + 24
1   libsystem_pthread.dylib         0x3a8ba224 _pthread_cond_wait + 536
2   libsystem_pthread.dylib         0x3a8bb000 pthread_cond_wait + 36
3   JavaScriptCore                  0x30fae23e JSC::GCThread::waitForNextPhase() + 74
4   JavaScriptCore                  0x30fae298 JSC::GCThread::gcThreadMain() + 48
5   JavaScriptCore                  0x30e103a8 WTF::wtfThreadEntryPoint(void*) + 12
6   libsystem_pthread.dylib         0x3a8bac1a _pthread_body + 138
7   libsystem_pthread.dylib         0x3a8bab8a _pthread_start + 98
8   libsystem_pthread.dylib         0x3a8b8c8c thread_start + 4

Thread 17 name:  JavaScriptCore::BlockFree
Thread 17:
0   libsystem_kernel.dylib          0x3a851f38 __psynch_cvwait + 24
1   libsystem_pthread.dylib         0x3a8ba224 _pthread_cond_wait + 536
2   libsystem_pthread.dylib         0x3a8bb040 pthread_cond_timedwait + 40
3   JavaScriptCore                  0x30e12eb8 WTF::ThreadCondition::timedWait(WTF::Mutex&, double) + 104
4   JavaScriptCore                  0x30e12ce4 JSC::BlockAllocator::blockFreeingThreadMain() + 88
5   JavaScriptCore                  0x30e103a8 WTF::wtfThreadEntryPoint(void*) + 12
6   libsystem_pthread.dylib         0x3a8bac1a _pthread_body + 138
7   libsystem_pthread.dylib         0x3a8bab8a _pthread_start + 98
8   libsystem_pthread.dylib         0x3a8b8c8c thread_start + 4

So... what's the problem with creating a JSContext on a global queue? What should I do differently to avoid the problem?

Hills answered 16/1, 2014 at 10:56 Comment(2)
I would actually suggest that you should avoid creating so many unique JSContexts if you can help it. Instead you should try to leverage a single JSContext (if possible!), instead of creating brand new JSContexts every time. Is it at all possible to leverage a single JSContext for your queue execution instead of creating a new JSContext with each queue item?Fully
But that is also just a naive statement, without really knowing the root of the reason why you need to create a new JSContext each queue item. If you'd like more insight into reusing an existing JSContext for multiple things, I could try to help with that!Fully
A
8

Luckily it's open source!

http://www.opensource.apple.com/source/JavaScriptCore/JavaScriptCore-7534.57.3/wtf/OSAllocatorPosix.cpp

void* OSAllocator::reserveAndCommit(size_t bytes, Usage usage, bool writable, bool executable, bool includesGuardPages)

tries to allocate a virtual machine, by allocating some memory

result = mmap(result, bytes, protection, flags, fd, 0);
    if (result == MAP_FAILED) {
       ...
            CRASH();
    }

the memory allocation fails and the app crashes.

Sooooo my best guess would be that this issue is popping up, due to a low memory situation.

How many of these are you allocating?

African answered 20/1, 2014 at 18:0 Comment(14)
It should only be one at a time; assuming they're not being cleared up when I think, there should have been maybe ten or twenty created by the time of the crash. It only happens on the customer's 32-bit device; perhaps it's related to this? #17922152Hills
Does it occur on iPhone 5 as well? 4S has half the RAM, so it might be that the OS is just memory starved, if it happens on iPhone5, it's probably the 32bit issue.African
I'll try to track down a 5 for testing on. If it is the 32bit issue, what can I do?Hills
Just tested it on iPhone 4 and it realiably crashes at the 600th to 1000th iteration/dispatch. Always precisely at 110,x mb allocated space. I will test it on iPhone 5 tommorow...i left the stupid new connector at home.Tobiastobie
Uhm, i tried it without the dispatch, same error. It always crashes at the 116th allocation...at 110 mb which seems to be the maximum a single app can allocate with iPhone 4; Every single JSContext allocation takes a freaking full megabyte of ram so i would guess its the ram rather than the 32 bit issue.Tobiastobie
Thanks! I've added the trace of what the other JS-related threads were doing, in case that's relevant.Hills
Well, the case here would be that there's just not enough memory to do everything at once on the 4S, so a couple of things that you might consider would be: 1. Every [[JSContext alloc] init] implicitly creates a JSVirtualMachine to work with, which takes up most of the memory, so you could try creating one JSVirtualMachine and using it with each of the JSContexts through [[JSContext alloc] initWithVirtualMachine:vm] 2. Consider implementing the didReceiveMemoryWarning methods and notifications and releasing anything unnecessary when those get hit.African
3. Limit the amount of contexts that you create on devices with less than 1GB memory. 4. Fire up Instruments and the allocations tool and see what's taking up the bulk of the memory usage at the time that you create these contexts and see if you could free up as much memory as possible before using the contextsAfrican
@African for (1), if I create just the one VM, will the JS variables and exceptions still be separate in separate JSContexts?Hills
Yes, they will be and every JSContext has its exception property. The only issue that might come out of this is that you need multiple VMs in order to do concurrent Javascript execution - is that a concern with you?African
@African I can try creating one per thread, and see whether that's less memory used overall.Hills
Just did the test with iPhone 5, the limit seems to be 231.x mb resulting in 246 allocations. As Andrew said, the JSVirtualMachine is taking up 99,9% of that as allocating with the same JSVirtualMachine can be done hundreds of thousands of times. I suggest the same as him, try to manage the VirtualMachines.Tobiastobie
I've tried changing the code to use just one JSVirtualMachine per thread; now I end up with tens of JavaScriptCore::BlockFree threads hanging around.Hills
@Tobiastobie other than the above, no - The excess of JavaScriptCore::BlockFree threads kill my unit tests.Hills
L
1

I experienced a similar issue. Ultimately the extra JavaScriptCore::BlockFree threads were a symptom of the JSContext not getting released. I'd suggest checking to make sure your JSVirtualMachine and JSContext are getting released at the end of execution.

Also as an aside, I am now successfully creating many JSVirtualMachines (1000+ during a run) without any issue, so I don't think that there is a problem with this unless you need a lot of these to run concurrently (like 50+).

Hope that helps!

Lixivium answered 23/2, 2014 at 22:23 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.