一個“反問”引發的內存反思

大家好,好久不見,最近正好有個iOS內存問題與大家分享

今天,論壇上有人突然發問,對線程ID獲取方式提出內存風險🤔,

作爲內存方面的興趣愛好者,第一時間湊了過來。。。🧐

問題很簡單,主要在於獲取線程ID的方式,做iOS的同學比較熟悉,一般來說是用:

[NSThread currentThread];

但在一些底層功能實現時,僅僅依靠OC層時不夠的,特別是在做一些HOOK,或者撈取一些系統數據時,還需要C/C++的實現,比如:

https://stackoverflow.com/questions/1540603/mac-iphone-is-there-a-way-to-get-a-thread-identifier-without-using-objective-c 有趣的是,這個問題下面,也有同學提出了類似的問題,

pthread_mach_thread_np(pthread_self()) 和mach_thread_self()都是獲取當前線程的ID,那兩個效果是否一樣呢?

首先,看.h文件

根據定義,我們發現pthread_mach_thread_np主要是將pthread_t的類型轉換成mach_port_t類型,這裏有個知識點,線程的類別,主要可分爲三種 1.glibc庫的線程id。實際上是線程控制塊tcb首地址;2.內核級線程id,系統唯一;3.mac os特有的id。實際上不能說是thread id,而應該當做是端口

https://blog.csdn.net/yxccc_914/article/details/79854603 這篇文章進行了一個簡單的講解,裏面也有一些鏈接講性能獲取的內容,大家可以參考

而mach_thread_self看起來就是直接獲取自身的mach_port_t端口號

至於NSThread 與上面兩這個關係,這裏推薦兩篇文章

https://blog.csdn.net/weixin_34268610/article/details/87963492這裏主要是講BSBacktraceLogger 這個輕量級的框架的源碼,裏面對NSThread和pthread進行了研究。

另一個是http://www.cocoachina.com/articles/11970,這個主要是講RunLoop原理的,裏面也有相關的內容

這兩篇文章本身知識點密集,推薦有空的同學看一下,下面言歸正傳

既然看.h文件看不出來,只能找找案例或者源碼、官方文檔

(這個過程很辛苦。。。由於一些原因,沒有梯子可以用,我在baidu浪費了幾個小時沒有任何結果,在快要崩潰的邊緣終於在bing搜索找到了答案。。。在線推薦bing一波)

首先看一個案例(截圖來源於網絡搜索,如有侵權請告知)

https://codereview.chromium.org/276043002/

在某項目的CR過程中,評審人不推薦使用mach_thread_self(),原因是需要mach_port_deallocate()配對釋放,而使用pthread_mach_thread_np(pthread_self())調用時線程池的緩存,不需要mach_port_deallocate()進行釋放,同時,配對使用mach_thread_self()和mach_port_deallocate()進行兩次系統調用,而pthread_mach_thread_np(pthread_self())只是調用了兩次libc function,更加輕量。

又進了一步,再去查查文檔,由於是mach層的內容,我這邊直接找了gnumach的文檔,畢竟看操作系統級的代碼還是要看對應的文檔更靠譜。

http://www.gnu.org/software/hurd/gnumach-doc/Thread-Information.html

這裏寫明瞭,mach_thread_self()會增加當前內容的引用計數,這裏就跟OC的內存機制很像,OC就是通過引用計數機制對內存進行管理,每次指針引用它,計數就會加一,使用完成後,需要手動釋放,當引用計數爲零時,就會釋放這個內存,這裏應該可以類比,值得注意的是後面這段話,這裏確保的方法的穩定性,畢竟底層方法的調用,穩定性是大家十分關心的。

再看看mach_port_deallocate()的定義

http://web.mit.edu/darwin/src/modules/xnu/osfmk/man/mach_port_deallocate.html

確實是用於減少引用計數

另外,在查找的過程中,偶然間在KSCRASH框架源碼中找到了下面內容

https://github.com/kstenerud/KSCrash

第一張是對mach_thread_self()的提醒,第二張是對mach_thread_self()進行了封裝,防止出現沒有配對的現象,KSCRASH本身就是獲取crash信息的第三方插件,自身的穩定性不言而喻,既然這趟封裝,說明用了就釋放對穩定性沒有影響。

有文檔,有案例,基本可以石錘了。

總結

mach_thread_self()是一個穩定的獲取線程ID的方法,但需要跟mach_port_deallocate()配對使用,不然可能會引發內存常駐

pthread_mach_thread_np(pthread_self())不要系統調用,也不要配對使用,推薦優先使用

如果因一些原因一定要使用mach_thread_self(),可以學習kscrash進行封裝。

 

課外拓展:

getid()和pthread_self()區別

https://stackoverflow.com/questions/6372102/what-is-the-difference-between-pthread-self-and-gettid-which-one-should-i-u

https://www.cnblogs.com/jaydenhpj/p/5200062.html

 

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章