Avoid memory trashing
I'm working on a Mac application that uses garbage collection. The application crashes for a few users and the crash logs indicate that开发者_StackOverflow it has something to do with memory corruption or memory trashing.
I post the important bit of the crash log below.
Exception Type: EXC_BAD_ACCESS (SIGSEGV)
Exception Codes: KERN_INVALID_ADDRESS at 0x0000000000000000
Crashed Thread: 0 Dispatch queue: com.apple.main-thread
Application Specific Information:
objc[81831]: garbage collection is ON
Thread 0 Crashed: Dispatch queue: com.apple.main-thread
0 libSystem.B.dylib 0x00007fffffe00847 __memcpy + 167
1 libauto.dylib 0x00007fff82718170 auto_zone_write_barrier_memmove + 96
2 libauto.dylib 0x00007fff8271916e auto_realloc(_malloc_zone_t*, void*, unsigned long) + 878
3 libSystem.B.dylib 0x00007fff8346e0db malloc_zone_realloc + 92
4 com.apple.Foundation 0x00007fff83169836 _NSMutableDataGrowBytes + 652
5 com.apple.Foundation 0x00007fff83169513 -[NSConcreteMutableData appendBytes:length:] + 101
6 MY.Application 0x000000010000b9cd -[Connection stream:handleEvent:] + 376
7 com.apple.CoreFoundation 0x00007fff85742373 _signalEventSync + 115
8 com.apple.CoreFoundation 0x00007fff857422e4 _cfstream_solo_signalEventSync + 116
What happens is, my application receives data from the network and writes that data to an NSMutableData object. I have talked about this with some other developers and our best guess is that memory is being trashed, which causes the crash.
The question is how do you prevent memory trashing and how do you debug bugs like this in Xcode?
For completeness, I also post the code of the method that leads up to the crash.
- (void)stream:(NSStream *)stream handleEvent:(NSStreamEvent)eventCode {
switch(eventCode) {
case NSStreamEventHasSpaceAvailable: {
if (stream == outputStream) {
[self writeBufferToStream];
}
break;
}
case NSStreamEventOpenCompleted:
if (stream == inputStream) {
readReady = YES;
} else {
writeReady = YES;
}
if ([self isReadyForUse] && [delegate respondsToSelector:@selector(connectionReadyForUse:)])
[delegate connectionReadyForUse:self];
break;
case NSStreamEventHasBytesAvailable: {
if (stream == inputStream) {
int bytesRead = 0;
static uint8_t buffer[kBufferSize];
bytesRead = [inputStream read:buffer maxLength:sizeof(buffer)];
[inBuffer appendBytes:buffer length:bytesRead];
//** Process buffer contents **//
BOOL safe = YES;
while (safe) {
if (inSize <= 0) {
if ([inBuffer length] >= sizeof(uint64_t)) {
memcpy(&inSize, [inBuffer bytes], sizeof(uint64_t));
NSRange rangeToDelete = {0, sizeof(uint64_t)};
[inBuffer replaceBytesInRange:rangeToDelete withBytes:NULL length:0];
} else {
break;
}
}
if (inSize > 0) {
if ([inBuffer length] >= inSize) {
NSMutableData *packetData = [NSMutableData dataWithBytes:[inBuffer bytes] length:inSize];
[delegate connection:self receivedData:packetData];
safe = NO;
NSRange rangeToDelete = {0, inSize};
[inBuffer replaceBytesInRange:rangeToDelete withBytes:NULL length:0];
inSize = 0;
} else {
break;
}
} else {
break;
}
}
}
break;
}
case NSStreamEventErrorOccurred: {
NSError *theError = [stream streamError];
if (stream == inputStream)
if (delegate && [delegate respondsToSelector:@selector(connection:encounteredReadError:)])
[delegate connection:self encounteredReadError:theError];
else{
if (delegate && [delegate respondsToSelector:@selector(connection:encounteredWriteError:)])
[delegate connection:self encounteredWriteError:theError];
}
break;
}
case NSStreamEventEndEncountered: {
if (delegate && [delegate respondsToSelector:@selector(connectionDisconnected:)])
[delegate connectionDisconnected:self];
readReady = NO;
writeReady = NO;
break;
}
default:
break;
}
}
keep your data reading and writing in same thread. If you must do it in multi thread, you can choose lock or barrier to make that thread safe
精彩评论