iOS内存问题:
IBOutlet为啥是weak的?因为subview添加到view上时,view会“拥有”subview。而uiviewcontroller是没有必要拥有subview。
认识IOS memory:
http://liam.flookes.com/wp/2012/05/03/finding-ios-memory/
http://stackoverflow.com/questions/32664679/what-is-the-limit-on-virtual-memory-for-ios
linux内存:
http://gityuan.com/2015/10/30/kernel-memory/
栈区:由系统自动分配,自动管理的区域。一般存放 局部变量,存放函数的参数值。变量名(不带*)相当于是指向栈区数据的指针别名。
堆区:是由alloc分配的内存全局区,堆是向高地址扩展的数据结构,是不连续的内存区域。这是由于系统是用链表来存储的空闲内存地址的,自然是不连续的,而链表的遍历方向是由低地址向高地址。堆的大小受限于计算机系统中有效的虚拟内存。由此可见,堆获得的空间比较灵活,也比较大。
常量区:
全局区(静态区): (static) 全局变量和静态变量的存储是放在一起的,初始化的全局变量和静态变量存放在一块区域(.data),未初始化的全局变量和静态变量在相邻的另一块区域(.bss),程序结束后由系统释放
代码区:
http://ibloodline.com/articles/2016/01/15/memory.html
低内存处理:
当物理内存上的free pages低于一定阈值,发出内存低的通知。your application must remove strong references to as many objects as possible. For example, you can use the warnings to clear out data caches that can be recreated later. View controllers automatically remove references to views that are currently offscreen
StackGuard是一种用来防止堆栈溢出攻击的编译器。由Crispin Cowan及ImmuniX开发小组维护。用它编译的程序可以有效的防止堆栈溢出问题。它的工作原理是在调用函数时,将返回地址压栈后,再将一个随机产生的字也压入堆栈。当函数调用完毕返回时,查看是否这个随机字被修改,若已被改动,则说明有溢出发生。自从1998年1月StackGuard推出以来,还没有发现它有任何的弱点。但不久前Mariusz Woloszyn([email protected])发现在特定的条件下,可以绕过StackGuard的保护机制,可能获得非法的特权。
内存缓存NSChche
内存异常:
Exception Type: EXC_BAD_ACCESS (SIGBUS)
Exception Type: EXC_CRASH (SIGABRT)
Exception Type: EXC_BREAKPOINT (SIGTRAP)
NSMallocException:分配过大内存
NSInternalInconsistencyException
man signal:看signal具体详情。
A SIGSEGV((Segmentation fault)) is a segmentation fault:The address exist, but your program does not have access to it. meaning you are trying to access an invalid memory address.---signal 11,a fault occurs because that address has not been mapped to physical memory。
Those exceptions (in fact, they are signals) are not related to Objective-C, but C. So you can get such an exception without Objective-C objects.
Note that a signal is not an exception, meaning you can't catch them with @try and @catch blocks.
You may set a signal handler with the signal and sigaction functions. Keep in mind some signals, like SIGABRT cannot be blocked.--signal 6
http://stackoverflow.com/questions/7446655/exception-types-in-ios-crash-logs(各种信号错误)
https://en.wikipedia.org/wiki/Signal.h 段错误,信号是C语言级别的问题,与OC无关。
http://www.5neo.be/understanding-ios-exception-types/
a SIGABRT exception is raised when an object receives an unimplemented message. Or to put it in simpler terms, there’s a call for a nonexistent method on an object.
iphone模拟器奔溃日志位置:~/资源库/Logs/DiagnosticReports
https://developer.apple.com/library/content/documentation/Cocoa/Conceptual/Multithreading/CreatingThreads/CreatingThreads.html
https://developer.apple.com/library/content/documentation/Performance/Conceptual/ManagingMemory/Articles/AboutMemory.html
out of memory问题: OOMs occur when the system runs low on memory and the OS kills the app to reclaim memory
https://code.facebook.com/posts/1146930688654547/reducing-fooms-in-the-facebook-ios-app/
http://stackoverflow.com/questions/79923/what-and-where-are-the-stack-and-heap
还有就是ARC还是MRR、“谁分配谁释放”说的都是堆上对象的管理;对于NSObject子类用ARC,而core foundation的由于是C语言的,基于结构体,所以不能ARC。LLVM compiler cannot handle the lifetime of Core Foundation objects in structs and unions as well as this explanation, due to C does not give us very good language tools for managing the lifetime of aggregates.
http://clang.llvm.org/docs/AutomaticReferenceCounting.html#ownership-qualified-fields-of-structs-and-unions
多线程中,每个线程都拥有不同的栈区,但是分享堆区。
数据段(data segment)通常是指用来存放程序中已初始化的全局变量的一块内存区域。The .data segment contains any global or static variables which have a pre-defined value and can be modified.
https://en.wikipedia.org/wiki/Data_segment
字节对齐:32位CPU为例,CPU只能对0,4,8,16这样的地址进行寻址。而很多32位CPU禁掉了地址线中的低2位A0,A1,这样他们的地址必须是4的倍数,否则会发送错误。当计算机数据线为32位时,一次读入4个地址范围的数据。当一个int变量存放在0-3的地址中时,CPU一次就内存操作就可以取得int变量的值。但是如果int变量存放在1-4的地址中呢? 根据上面条件2的解释,这个时候CPU需要进行2次内存访问,第一次读取0-4的数据,并且只保存1-3的内容,第二次访问读取4-7的数据并且只保存4的数据,然后将1-4组合起来。
+(void)load:对于加入运行期系统中的每个类(class)及分类(category)来说,必定会调用此方法,而且仅调用一次。当包含类或分类的程序库载入系统时,就会执行此方法(通常指应用程序启动)。load 方法, 会在每一个类甚至分类被引入时仅调用一次, 调用的顺序是父类优先于子类, 子类优先于分类. 而且 load 方法不会被类自动继承,
+(void)initlize:只有当程序用到了相关的类时,才会调用。因此,如果某个类一直都没有使用,那么其initialize方法就一直不会运行。这也就等于说,应用程序无须先把每个类的initialize都执行一遍
http://www.cocoachina.com/ios/20161110/18028.html(1)ISA 指针:nsobject也是个类实例,所以必定属于某个类,这个类就叫原类metaclass
http://blog.devtang.com/2013/10/15/objective-c-object-model/
INSTRUMENTS的使用
instruments,allocation使用
https://developer.apple.com/library/content/technotes/tn2434/_index.html#//apple_ref/doc/uid/DTS40017252-CH1-PROFILE
Persistent Bytes : the total number of bytes your app currently holds in memory.if the device is low on available memory and your app is high on Persistent Bytes it can result in your app being terminated.
Persistent :It represents the total number of repetitions of a particular allocation.
错误的检测:
Enabling Guard Malloc:(Memory Usage Performance Guidelines)it places separate memory allocations on different virtual memory pages and then deletes the entire page when the memory is freed. Subsequent attempts to access the deallocated memory cause an immediate memory exception rather than a blind access into memory that might now hold other data. When the crash occurs, you can then go and inspect the point of failure in the debugger to identify the problem.
Address Sanitizer:
问题:char *ptr = malloc(5); ptr[15] = 0; OK
char *ptr = malloc(5); ptr[16] = 33; 有可能Crash,有可能不Crash
Address Sanitizer解决下列问题,原理:在需要检测的内存区域单元(可单独访问的内存区域)前后增加poisoned memory,:
Out-of-bounds accesses to heap, stack and globals
Use-after-free
Use-after-return (to some extent)
Double-free, invalid free
Memory leaks (experimental)
Enable Malloc Scribble
申请内存后在申请的内存上填0xAA,内存释放后在释放的内存上填0x55;再就是说如果内存未被初始化就被访问,或者释放后被访问,就会引发异常,这样就可以使问题尽快暴漏出来。
AddressSanitizer的原理是当程序创建变量分配一段内存时,将此内存后面的一段内存也冻结住,标识为中毒内存。当程序访问到中毒内存时(越界访问),就会抛出异常,并打印出相应log信息。调试者可以根据中断位置和的log信息,识别bug。如果变量释放了,变量所占的内存也会标识为中毒内存,这时候访问这段内存同样会抛出异常(访问已经释放的对象)。
Apple平台上,malloc函数总是最少分配16个字节,即使你申请少于16字节的空间,因此这段代码在Apple平台上运行正常,但不要依赖系统的这个特性)。这段错误代码可能危害不大,也可能后患无穷。
malloc方法:
(1)小内存分配,是16个字节的倍数
(2)大内存分配,是4K(一页)的倍数。
https://developer.apple.com/library/content/documentation/Performance/Conceptual/ManagingMemory/Articles/MemoryAlloc.html
https://developer.apple.com/library/prerelease/content/documentation/Cocoa/Conceptual/MemoryMgmt/Articles/MemoryMgmt.html#//apple_ref/doc/uid/10000011i
http://www.cocoachina.com/ios/20150729/12830.html
苹果官方文档https://developer.apple.com/library/content/documentation/Performance/Conceptual/ManagingMemory/Articles/MallocDebug.html
dealloc执行后只是告诉系统,这片内存我不用了,而系统并没有就让这片内存不能访问。访问野指针是不会Crash的,只有野指针指向的地址被写上了有问题的数据才会引发Crash
说说iOS与内存管理(上中下):
使用size 可以看到静态库的内存发布,也可以使用 clang XXX.c -0 YYY size YYY查看自己编写的.c代码的内存分布。
Anatomy of a Program in Memory:http://duartes.org/gustavo/blog/post/anatomy-of-a-program-in-memory/
heap-use-after-free:
https://www.owasp.org/index.php/Using_freed_memory
Two common reasons that lead to dangling pointers are:
Not updating the reference count of a currently in-use object. This results in the object currently in-use to be prematurely freed.
Not updating a pointer value once the object it points to is freed.
内存管理的文章:http://js.sunansheng.com/p/8e764d05275b
http://liam.flookes.com/wp/2012/05/03/finding-ios-memory/
计算机是32位,那么它的地址总线是32位的,也就是它可以寻址00xFFFFFFFF(4G)的地址空间,但如果你的计算机只有256M的物理内存0x0x0FFFFFFF(256M),同时你的进程产生了一个不在这256M地址空间中的地址,那么计算机该如何处理呢?
计算机分页机制:
计算机会对虚拟内存地址空间(32位为4G)分页产生页(page),对物理内存地址空间(假设256M)分页产生页帧(page frame),这个页和页帧的大小是一样大的,所以呢,在这里,虚拟内存页的个数势必要大于物理内存页帧的个数。在计算机上有一个页表(page table),就是映射虚拟内存页到物理内存页的,更确切的说是页号到页帧号的映射,而且是一对一的映射。但是问题来了,虚拟内存页的个数 > 物理内存页帧的个数,岂不是有些虚拟内存页的地址永远没有对应的物理内存地址空间?不是的,操作系统是这样处理的。操作系统有个页面失效(page fault)功能。操作系统找到一个最少使用的页帧,让他失效,并把它写入磁盘,随后把需要访问的页放到页帧中,并修改页表中的映射,这样就保证所有的页都有被调度的可能了。这就是处理虚拟内存地址到物理内存的步骤。现在来回答什么是虚拟内存地址和物理内存地址。虚拟内存地址由页号(与页表中的页号关联)和偏移量组成。页号就不必解释了,上面已经说了,页号对应的映射到一个页帧。那么,说说偏移量。偏移量就是我上面说的页(或者页帧)的大小,即这个页(或者页帧)到底能存多少数据。举个例子,有一个虚拟地址它的页号是4,偏移量是20,那么他的寻址过程是这样的:首先到页表中找到页号4对应的页帧号(比如为8),如果页不在内存中,则用失效机制调入页,否则把页帧号和偏移量传给MMC(CPU的内存管理单元)组成一个物理上真正存在的地址,接着就是访问物理内存中的数据了。总结起来说,虚拟内存地址的大小是与地址总线位数相关,物理内存地址的大小跟物理内存条的容量相关。
虚拟内存:https://developer.apple.com/library/content/documentation/Performance/Conceptual/ManagingMemory/Articles/AboutMemory.html
your code is probably trying to use an already-freed object or memory buffer. Set the NSZombieEnabled(在引用计数降到0时,该僵尸实现会将该对象转换成僵尸对象) environment variable to a YES/a to find messages to already freed objects.
instruments使用指南:
http://www.cocoachina.com/swift/20150623/12237.html
关于对象和runtime对象的释放时机问题:
https://github.com/ChenYilong/iOSInterviewQuestions/blob/master/01《招聘一个靠谱的iOS》面试题参考答案/《招聘一个靠谱的iOS》面试题参考答案(上).md
heap buffer overflow detected的错误:
https://developer.apple.com/library/content/documentation/Security/Conceptual/SecureCodingGuide/Articles/BufferOverflows.html
模拟ARC的例子--》https://github.com/ibireme/yy_music_base/blob/master/yy_music_base/ym_array.c
VMMAP命令可以查看App虚拟内存的情况。
vm_stat:
Secure Coding Guide:
guanAvoiding Buffer Overflows and Underflows
BLOCK的内存管理:
--NSGlobalBlock:位于内存全局区-》没有引用局部变量的block叫做NSGlobalBlock
--NSStackBlock:位于内存栈区-》引用了局部变量的block叫做NSStackBlock
--NSMallocBlock:位于内存堆区-》从栈区复制(copy)过来的block称为堆区block
不管是对block进行retain,copy,release,block的引用计数都不会增加,始终为1;
为何系统不使用栈区的block,而编译器使用copy,将其置于堆区域呢?
答:因为创建在stack上的block,当方法结束时刻,会被销毁,所以为了以后使用它,必须将其copy到heap上。
唐巧的:http://blog.devtang.com/2013/07/28/a-look-inside-blocks/
malloc:
calloc:
alloca:在栈(stack)上申请空间,用完马上就释放
jetsam机制:管理内存,https://www.quora.com/What-is-the-iOS-jetsam-and-how-does-it-exactly-work
iOS中的copy,mutableCopy:https://www.zybuluo.com/MicroCai/note/50592
copy返回imutable对象;所以,如果对copy返回值使用mutable对象接口就会crash;
mutableCopy返回mutable对象;
(1)非集合对象,只有[immutableObject copy] 是浅复制,因为他已经是非可变的了。没有必要再产生一个对象。
(2)集合对象,只有[immutableObject copy] 是浅复制,因为他已经是非可变的了。没有必要再产生一个对象。其它都是单层深复制。
MMAP:内存映射。当向内存中加载比较大的文件时候,比如100MB的文件,很有可能会把内存爆掉,文件内存映射是指把一个文件的内容映射到进程的内存虚拟地址空间中,这个实际上并没有为文件内容分配物理内存。实际上就相当于将内存地址值指向文件的磁盘地址。如果对这些内存进行读写,实际上就是对文件在磁盘上内容进行读写。
- (id)dataWithContentsOfFile:(NSString *)path options:(NSDataReadingOptions)readOptionsMask error:(NSError **)errorPtr;