Objective-C autorelease

  1. // main.m
  2. int main(int argc, char * argv[]) {
  3. @autoreleasepool {
  4.  
  5. }
  6. }

  

  1. clang -rewrite-objc main.m
  1. __AtAutoreleasePool __autoreleasepool;
  1. struct __AtAutoreleasePool {
  2. __AtAutoreleasePool() {atautoreleasepoolobj = objc_autoreleasePoolPush();}
  3. ~__AtAutoreleasePool() {objc_autoreleasePoolPop(atautoreleasepoolobj);}
  4. void * atautoreleasepoolobj;
  5. };
  1. class AutoreleasePoolPage
  2. {
  3.  
  4. #define POOL_SENTINEL 0
  5. static pthread_key_t const key = AUTORELEASE_POOL_KEY;
  6. static uint8_t const SCRIBBLE = 0xA3; // 0xA3A3A3A3 after releasing
  7. static size_t const SIZE =
  8. #if PROTECT_AUTORELEASEPOOL
  9. 4096; // must be multiple of vm page size
  10. #else
  11. 4096; // size and alignment, power of 2
  12. #endif
  13. static size_t const COUNT = SIZE / sizeof(id);
  14.  
  15. magic_t const magic;
  16. id *next;
  17. pthread_t const thread;
  18. AutoreleasePoolPage * const parent;
  19. AutoreleasePoolPage *child;
  20. uint32_t const depth;
  21. uint32_t hiwat;
  22.  
  23. // SIZE-sizeof(*this) bytes of contents follow
  24.  
  25. static void * operator new(size_t size) {
  26. return malloc_zone_memalign(malloc_default_zone(), SIZE, SIZE);
  27. }
  28. static void operator delete(void * p) {
  29. return free(p);
  30. }
  31.  
  32. inline void protect() {
  33. #if PROTECT_AUTORELEASEPOOL
  34. mprotect(this, SIZE, PROT_READ);
  35. check();
  36. #endif
  37. }
  38.  
  39. inline void unprotect() {
  40. #if PROTECT_AUTORELEASEPOOL
  41. check();
  42. mprotect(this, SIZE, PROT_READ | PROT_WRITE);
  43. #endif
  44. }
  45.  
  46. AutoreleasePoolPage(AutoreleasePoolPage *newParent)
  47. : magic(), next(begin()), thread(pthread_self()),
  48. parent(newParent), child(NULL),
  49. depth(parent ? 1+parent->depth : 0),
  50. hiwat(parent ? parent->hiwat : 0)
  51. {
  52. if (parent) {
  53. parent->check();
  54. assert(!parent->child);
  55. parent->unprotect();
  56. parent->child = this;
  57. parent->protect();
  58. }
  59. protect();
  60. }
  61.  
  62. ~AutoreleasePoolPage()
  63. {
  64. check();
  65. unprotect();
  66. assert(empty());
  67.  
  68. // Not recursive: we don't want to blow out the stack
  69. // if a thread accumulates a stupendous amount of garbage
  70. assert(!child);
  71. }
  72.  
  73. void busted(bool die = true)
  74. {
  75. (die ? _objc_fatal : _objc_inform)
  76. ("autorelease pool page %p corrupted\n"
  77. " magic 0x%08x 0x%08x 0x%08x 0x%08x\n pthread %p\n",
  78. this, magic.m[0], magic.m[1], magic.m[2], magic.m[3],
  79. this->thread);
  80. }
  81.  
  82. void check(bool die = true)
  83. {
  84. if (!magic.check() || !pthread_equal(thread, pthread_self())) {
  85. busted(die);
  86. }
  87. }
  88.  
  89. void fastcheck(bool die = true)
  90. {
  91. if (! magic.fastcheck()) {
  92. busted(die);
  93. }
  94. }
  95.  
  96. id * begin() {
  97. return (id *) ((uint8_t *)this+sizeof(*this));
  98. }
  99.  
  100. id * end() {
  101. return (id *) ((uint8_t *)this+SIZE);
  102. }
  103.  
  104. bool empty() {
  105. return next == begin();
  106. }
  107.  
  108. bool full() {
  109. return next == end();
  110. }
  111.  
  112. bool lessThanHalfFull() {
  113. return (next - begin() < (end() - begin()) / 2);
  114. }
  115.  
  116. id *add(id obj)
  117. {
  118. assert(!full());
  119. unprotect();
  120. *next++ = obj;
  121. protect();
  122. return next-1;
  123. }
  124.  
  125. void releaseAll()
  126. {
  127. releaseUntil(begin());
  128. }
  129.  
  130. void releaseUntil(id *stop)
  131. {
  132. // Not recursive: we don't want to blow out the stack
  133. // if a thread accumulates a stupendous amount of garbage
  134.  
  135. while (this->next != stop) {
  136. // Restart from hotPage() every time, in case -release
  137. // autoreleased more objects
  138. AutoreleasePoolPage *page = hotPage();
  139.  
  140. // fixme I think this `while` can be `if`, but I can't prove it
  141. while (page->empty()) {
  142. page = page->parent;
  143. setHotPage(page);
  144. }
  145.  
  146. page->unprotect();
  147. id obj = *--page->next;
  148. memset((void*)page->next, SCRIBBLE, sizeof(*page->next));
  149. page->protect();
  150.  
  151. if (obj != POOL_SENTINEL) {
  152. objc_release(obj);
  153. }
  154. }
  155.  
  156. setHotPage(this);
  157.  
  158. #ifndef NDEBUG
  159. // we expect any children to be completely empty
  160. for (AutoreleasePoolPage *page = child; page; page = page->child) {
  161. assert(page->empty());
  162. }
  163. #endif
  164. }
  165.  
  166. void kill()
  167. {
  168. // Not recursive: we don't want to blow out the stack
  169. // if a thread accumulates a stupendous amount of garbage
  170. AutoreleasePoolPage *page = this;
  171. while (page->child) page = page->child;
  172.  
  173. AutoreleasePoolPage *deathptr;
  174. do {
  175. deathptr = page;
  176. page = page->parent;
  177. if (page) {
  178. page->unprotect();
  179. page->child = NULL;
  180. page->protect();
  181. }
  182. delete deathptr;
  183. } while (deathptr != this);
  184. }
  185.  
  186. static void tls_dealloc(void *p)
  187. {
  188. // reinstate TLS value while we work
  189. setHotPage((AutoreleasePoolPage *)p);
  190. pop(0);
  191. setHotPage(NULL);
  192. }
  193.  
  194. static AutoreleasePoolPage *pageForPointer(const void *p)
  195. {
  196. return pageForPointer((uintptr_t)p);
  197. }
  198.  
  199. static AutoreleasePoolPage *pageForPointer(uintptr_t p)
  200. {
  201. AutoreleasePoolPage *result;
  202. uintptr_t offset = p % SIZE;
  203.  
  204. assert(offset >= sizeof(AutoreleasePoolPage));
  205.  
  206. result = (AutoreleasePoolPage *)(p - offset);
  207. result->fastcheck();
  208.  
  209. return result;
  210. }
  211.  
  212. static inline AutoreleasePoolPage *hotPage()
  213. {
  214. AutoreleasePoolPage *result = (AutoreleasePoolPage *)
  215. tls_get_direct(key);
  216. if (result) result->fastcheck();
  217. return result;
  218. }
  219.  
  220. static inline void setHotPage(AutoreleasePoolPage *page)
  221. {
  222. if (page) page->fastcheck();
  223. tls_set_direct(key, (void *)page);
  224. }
  225.  
  226. static inline AutoreleasePoolPage *coldPage()
  227. {
  228. AutoreleasePoolPage *result = hotPage();
  229. if (result) {
  230. while (result->parent) {
  231. result = result->parent;
  232. result->fastcheck();
  233. }
  234. }
  235. return result;
  236. }
  237.  
  238. static inline id *autoreleaseFast(id obj)
  239. {
  240. AutoreleasePoolPage *page = hotPage();
  241. if (page && !page->full()) {
  242. return page->add(obj);
  243. } else {
  244. return autoreleaseSlow(obj);
  245. }
  246. }
  247.  
  248. static __attribute__((noinline))
  249. id *autoreleaseSlow(id obj)
  250. {
  251. AutoreleasePoolPage *page;
  252. page = hotPage();
  253.  
  254. // The code below assumes some cases are handled by autoreleaseFast()
  255. assert(!page || page->full());
  256.  
  257. if (!page) {
  258. assert(obj != POOL_SENTINEL);
  259. _objc_inform("Object %p of class %s autoreleased "
  260. "with no pool in place - just leaking - "
  261. "break on objc_autoreleaseNoPool() to debug",
  262. obj, object_getClassName(obj));
  263. objc_autoreleaseNoPool(obj);
  264. return NULL;
  265. }
  266.  
  267. do {
  268. if (page->child) page = page->child;
  269. else page = new AutoreleasePoolPage(page);
  270. } while (page->full());
  271.  
  272. setHotPage(page);
  273. return page->add(obj);
  274. }
  275.  
  276. public:
  277. static inline id autorelease(id obj)
  278. {
  279. assert(obj);
  280. assert(!OBJC_IS_TAGGED_PTR(obj));
  281. id *dest __unused = autoreleaseFast(obj);
  282. assert(!dest || *dest == obj);
  283. return obj;
  284. }
  285.  
  286. static inline void *push()
  287. {
  288. if (!hotPage()) {
  289. setHotPage(new AutoreleasePoolPage(NULL));
  290. }
  291. id *dest = autoreleaseFast(POOL_SENTINEL);
  292. assert(*dest == POOL_SENTINEL);
  293. return dest;
  294. }
  295.  
  296. static inline void pop(void *token)
  297. {
  298. AutoreleasePoolPage *page;
  299. id *stop;
  300.  
  301. if (token) {
  302. page = pageForPointer(token);
  303. stop = (id *)token;
  304. assert(*stop == POOL_SENTINEL);
  305. } else {
  306. // Token 0 is top-level pool
  307. page = coldPage();
  308. assert(page);
  309. stop = page->begin();
  310. }
  311.  
  312. if (PrintPoolHiwat) printHiwat();
  313.  
  314. page->releaseUntil(stop);
  315.  
  316. // memory: delete empty children
  317. // hysteresis: keep one empty child if this page is more than half full
  318. // special case: delete everything for pop(0)
  319. if (!token) {
  320. page->kill();
  321. setHotPage(NULL);
  322. } else if (page->child) {
  323. if (page->lessThanHalfFull()) {
  324. page->child->kill();
  325. }
  326. else if (page->child->child) {
  327. page->child->child->kill();
  328. }
  329. }
  330. }
  331.  
  332. static void init()
  333. {
  334. int r __unused = pthread_key_init_np(AutoreleasePoolPage::key,
  335. AutoreleasePoolPage::tls_dealloc);
  336. assert(r == 0);
  337. }
  338.  
  339. void print()
  340. {
  341. _objc_inform("[%p] ................ PAGE %s %s %s", this,
  342. full() ? "(full)" : "",
  343. this == hotPage() ? "(hot)" : "",
  344. this == coldPage() ? "(cold)" : "");
  345. check(false);
  346. for (id *p = begin(); p < next; p++) {
  347. if (*p == POOL_SENTINEL) {
  348. _objc_inform("[%p] ################ POOL %p", p, p);
  349. } else {
  350. _objc_inform("[%p] %#16lx %s",
  351. p, (unsigned long)*p, object_getClassName(*p));
  352. }
  353. }
  354. }
  355.  
  356. static void printAll()
  357. {
  358. _objc_inform("##############");
  359. _objc_inform("AUTORELEASE POOLS for thread %p", pthread_self());
  360.  
  361. AutoreleasePoolPage *page;
  362. ptrdiff_t objects = 0;
  363. for (page = coldPage(); page; page = page->child) {
  364. objects += page->next - page->begin();
  365. }
  366. _objc_inform("%llu releases pending.", (unsigned long long)objects);
  367.  
  368. for (page = coldPage(); page; page = page->child) {
  369. page->print();
  370. }
  371.  
  372. _objc_inform("##############");
  373. }
  374.  
  375. static void printHiwat()
  376. {
  377. // Check and propagate high water mark
  378. // Ignore high water marks under 256 to suppress noise.
  379. AutoreleasePoolPage *p = hotPage();
  380. uint32_t mark = p->depth*COUNT + (uint32_t)(p->next - p->begin());
  381. if (mark > p->hiwat && mark > 256) {
  382. for( ; p; p = p->parent) {
  383. p->unprotect();
  384. p->hiwat = mark;
  385. p->protect();
  386. }
  387.  
  388. _objc_inform("POOL HIGHWATER: new high water mark of %u "
  389. "pending autoreleases for thread %p:",
  390. mark, pthread_self());
  391.  
  392. void *stack[128];
  393. int count = backtrace(stack, sizeof(stack)/sizeof(stack[0]));
  394. char **sym = backtrace_symbols(stack, count);
  395. for (int i = 0; i < count; i++) {
  396. _objc_inform("POOL HIGHWATER: %s", sym[i]);
  397. }
  398. free(sym);
  399. }
  400. }
  401.  
  402. #undef POOL_SENTINEL
  403. };

  

https://opensource.apple.com/source/objc4/objc4-532/runtime/NSObject.mm.auto.html

@autoreleasepool

双向链表

Objective-C autoreleasepool深入理解的更多相关文章

  1. YYKit @autoreleasepool 使用,优化内存

    写在前面 最近再看YY大神的YYKit工具,发现在代码中经常使用@autoreleasepool,特别是在与for循环搭配使用的时候.刚开始很不能理解. 先有个概念: 自己创建的对象:使用 alloc ...

  2. objective-c(内存管理)

    本文主要记录objective-c 内存管理的知识点: 1.objective-c的对象都是分配内存在堆上,与C的mallock和C++的new类似,只有int等系统变量分配内存在栈上: 2.obje ...

  3. iOS - OC 内存管理

    1.OC 基本内存管理模型 1.1 自动垃圾收集 在 OC 2.0 中,有一种称为垃圾收集的内存管理形式.通过垃圾收集,系统能够自动监测对象是否拥有其他的对象,当程序执行需要空间的时候,不再被引用的对 ...

  4. [UWP] 对应用进行A/B测试

    [对A/B测试的看法] 开发者在Dev Center中设置几种应用变体,这几种变体有几个变量的值不一样,比如有变体A和变体B(当然还可以加上变体C,Dev Center最多支持5个变体),A和B的不同 ...

  5. Cocos2d-X3.0 刨根问底(四)----- 内存管理源码分析

    本系列文章发表以来得到了很多朋友的关注,小鱼在这里谢谢大家对我的支持,我会继续努力的,最近更新慢了一点,因为我老婆流产了抽了很多时间来照顾她希望大家谅解,并在此预祝我老婆早日康复. 上一篇,我们完整的 ...

  6. C --> OC with RunTime

    前言 本来打算写一篇关于runtime的学习总结,无奈长篇大论不是我的风格,就像写申论一样痛苦,加之网上关于tuntime的文章多如牛毛,应该也够童子们学习的了,今天就随便聊聊我的理解吧. runti ...

  7. Automake

    Automake是用来根据Makefile.am生成Makefile.in的工具 标准Makefile目标 'make all' Build programs, libraries, document ...

  8. Objective C内存管理之理解autorelease------面试题

    Objective C内存管理之理解autorelease   Autorelease实际上只是把对release的调用延迟了,对于每一个Autorelease,系统只是把该Object放入了当前的A ...

  9. 理解Objective C 中id

    什么是id,与void *的区别 id在Objective C中是一个类型,一个complier所认可的Objective C类型,跟void *是不一样的,比如一个 id userName, 和vo ...

随机推荐

  1. webpack4 系列教程(九): CSS Tree Shaking

    教程所示图片使用的是 github 仓库图片,网速过慢的朋友请移步原文地址 有空就来看看个人技术小站, 我一直都在 0. 课程介绍和资料 本次课程的代码目录(如下图所示): >>> ...

  2. MEF 插件式开发之 DotNetCore 初体验

    背景叙述 在传统的基于 .Net Framework 框架下进行的 MEF 开发,大多是使用 MEF 1,对应的命名空间是 System.ComponentModel.Composition.在 Do ...

  3. kafka安装与简单使用

    一.kafka安装 安装是非常简单的,现在推荐安装0.8的版本,这个版本是非常稳定的,而且公司里面也多用此版本. 简单的安装: 这个是我使用的版本,kafka_2.11-0.8.2.2.tgz 直接t ...

  4. VUE页面刷新问题

    1). location方式 location.reload() 缺点:刷新页面,卡白 2). router方式 this.$router.go(0) 缺点:同一问题,比一好点 3). provide ...

  5. video 铺满父元素(object-fit: fill;)

    遇到这个属性,是在给video 嵌入一个div时,导致video播放器上下有灰色.在控制台查看video默认样式的时候看到了这个属性. 播放器上下的灰色,不是我们想要的样式,如果能完全覆盖就更好了. ...

  6. Android basics

    只要是Android中的控件,最终都继承自View.

  7. Fiddler抓包使用教程-过滤

    转载请标明出处:http://blog.csdn.net/zhaoyanjun6/article/details/72929800 本文出自[赵彦军的博客] Fiddler抓包可以完成我们移动开发者的 ...

  8. HttpClient与浏览器调用服务接口差异

    我用httpclient访问接口,统计图有些不均匀,差距较大 ,有时只有几十毫秒,下图看到这种情况占多数,600-800毫秒之间的算是浏览器正常的产生调用接口的时间耗时 然后用jmeter跑时都是均值 ...

  9. 洗礼灵魂,修炼python(60)--爬虫篇—httplib2模块

    这里先要补充一下,Python3自带两个用于和HTTP web 服务交互的标准库(内置模块): http.client 是HTTP协议的底层库 urllib.request 建立在http.clien ...

  10. ABAP性能和优化

    哪些工具可以用于性能优化? ST05-性能追踪.包含SQL追踪加RFC,队列和缓存追踪.SQL追踪主要用于测量程序中select语句的性能. SE30-运行时分析.用于测量应用的性能. SAT是过时的 ...