記一次PHP與自動(dòng)釋放池的內(nèi)存釋放的坑

使用多線程遍歷或操作集合對(duì)象的時(shí)候,務(wù)必要注意內(nèi)存的及時(shí)釋放,否則一不小心就觸發(fā)內(nèi)存峰值而閃退了。

最近新版本發(fā)布后,有較多用戶反饋閃退;經(jīng)過(guò)部分走訪用戶初步得出結(jié)論是內(nèi)存閃退,而且和PHPLibrary相關(guān)修改有關(guān);

聯(lián)系用戶

聯(lián)系了部分用戶,撈取到較多日志;確定原因基本是內(nèi)存閃退,發(fā)生閃退時(shí)瀏覽器占用了1.3G左右的內(nèi)存,超過(guò)了內(nèi)存限制而被jetsam殺死;這個(gè)不太合理,我們基本不會(huì)這樣瘋狂申請(qǐng)這么大內(nèi)存而不釋放的。

最后發(fā)現(xiàn)用戶都有一個(gè)共性,相冊(cè)圖片都很多。而恰好我們?cè)谛掳姹居袀€(gè)較大的修改為了適配iOS12而全面廢棄了ALAssetLibrary,所以懷疑是這里。

分析

給用戶打包集成了QAPM后,終于找到問(wèn)題禍?zhǔn)琢?,原?lái)是PHP的localIdentifier接口導(dǎo)致的


請(qǐng)?jiān)谶@里填寫圖片描述

找到PHP接口說(shuō)明,


請(qǐng)?jiān)谶@里填寫圖片描述

每次調(diào)用localIdentifier會(huì)觸發(fā)一次deep copy字符串,從而分配了內(nèi)存;但是一個(gè)字符串也不可能這么容易導(dǎo)致內(nèi)存飆漲呢?

定位

原因是我們?cè)跀?shù)組遍歷中,頻繁觸發(fā)了localIdentifier接口的調(diào)用,根據(jù)ARC說(shuō)明,不是new,alloc,等創(chuàng)建的對(duì)象,不是屬于我們管理的,因此該接口返回的是一個(gè)autorelease的對(duì)象,其釋放是由autoreleasepool統(tǒng)一釋放的。
而我們的代碼是如下調(diào)用的:

[albums enumerateObjectsWithOptions:NSEnumerationReverse usingBlock:^(PHAsset*  _Nonnull result, NSUInteger idx, BOOL * _Nonnull stop) {
                if (result) {
                    @synchronized (self.lockFileList) {
                        NSArray *arr=[_fileList copy];
                        for (int i = 0; i < arr.count; i++) {
                            MttPictureFile *file = arr[i];
                            if ([file.assetURL.absoluteString isEqual:result.localIdentifier]) {
                                *stop = YES;
                                return;
                            }
                        }
                    }

                    for (int i = 0; i < self.videoFiles.count; i++) {
                        MttAlbumVideoFile *file = self.videoFiles[i];
                        if ([file.assetURL.absoluteString isEqual:result.localIdentifier]) {
                            *stop = YES;
                            return;
                        }
                    }
                    
                    [self addPictureFileWithAsset:result];
                }
                ///...其余代碼
}];

存在2個(gè)問(wèn)題,

1.數(shù)組元素copy沒(méi)有及時(shí)釋放,這個(gè)還好,不會(huì)影響太大,但是遍歷的時(shí)候訪問(wèn)了localIdentifier,這個(gè)會(huì)導(dǎo)致極端的情況下,觸發(fā)上萬(wàn)個(gè)localIdentifier創(chuàng)建

2.另一個(gè)更嚴(yán)重的問(wèn)題,經(jīng)過(guò)反匯編調(diào)試,發(fā)現(xiàn)
[PHPFetchResult enumerateObjectsWithOptions:usingBlock:] 這里踩坑了,這里不帶autoreleasepool而導(dǎo)致內(nèi)存一直增加直到PHP遍歷執(zhí)行完畢后的autoreleasepool統(tǒng)一回調(diào)了才統(tǒng)一釋放。

以上2點(diǎn)會(huì)導(dǎo)致,當(dāng)用戶有1萬(wàn)張圖時(shí),假設(shè)_fileList為5000,即用戶一次性新增了5千張圖,而原先有5000張圖,那么這次回到就會(huì)觸發(fā)5000 * 5000 * 2次localIdentifier的創(chuàng)建,這些遍歷均會(huì)等到GCD block執(zhí)行完畢才統(tǒng)一釋放;這里就輕松的能占用幾百M(fèi)B到上千MB內(nèi)存了

結(jié)論

  1. 使用數(shù)組遍歷時(shí),切記多添加autoreleasepool
    [NSArray enumerateObjectsWithOptions:usingBlock:] 防止踩坑系統(tǒng)的各種array

  2. 對(duì)于非自己alloc,new的autorelease對(duì)象,要及時(shí)@autoreleasepool包裹讓其盡早釋放;

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容