我的 AVPlayer 的内存在哪里?如何取回它?

2024-02-08

我正在同时播放大量视频AVPlayer。为了减少加载时间,我将相应的视图存储在NSCache.

这种方法效果很好,直到达到一定数量的视频,视频就会停止播放,甚至不再出现。

没有错误、日志或内存警告。特别是,我正在听UIApplicationDidReceiveMemoryWarningNotification清除缓存,但这从未收到。

如果我删除缓存,所有视频都会播放,但性能会下降。

这让我怀疑AVPlayer正在使用来自不同进程的内存(哪个进程?)。当内存达到一定限度时,新玩家就会停止工作。

它是否正确?

如果是这样,是否有办法在达到此神奇限制时收到通知,以采取适当的措施(例如清除缓存)以确保其他媒体的播放?


好消息和坏消息 - 好消息是您可能可以解决问题,坏消息是它需要工作并且有些复杂。

根本问题

你没有提前收到通知的原因是,iOS 直到几乎为时已晚才发现你的应用程序已超出其内存预算,然后立即杀死它。该问题与 iOS(和 OS X)管理文件系统缓存的方式有关。通常,当文件打开时,当您读取数据时,文件数据会传输到缓冲区中Unified Buffer Cache(您可以通过谷歌搜索该术语以获取更多信息)-从现在起我将其称为 UBC。

因此,假设您有 10 个打开的文件,并且您已读完每个文件,但尚未关闭这些文件。嗯,所有这些数据都保存在 UBC 中。现在,如果关闭文件,缓冲区将全部被释放。从技术上讲,操作系统也可以清除这些缓冲区 - 只是当它意识到内存紧张时,它会选择首先清除应用程序(并且可能有充分的理由这样做)。因此,想象一下您的应用程序正在显示视频,并且加载视频的方式是通过文件系统,可用缓冲区的数量开始下降。在某些时候,iOS 会注意到这一点,追踪谁最属于(您的应用程序),然后尽快杀死您的应用程序。

我自己在我支持的开源项目中遇到了这个问题,相片卷轴网络 https://github.com/dhoerl/PhotoScrollerNetwork。用户开始抱怨他们的项目像您一样被系统终止,而没有任何通知。我尝试监控 UBC,但没有成功(OS X 上有 API 可以这样做,但 iOS 上没有)。最后我找到了一个使用启发式的解决方案 - 监视所有内存使用情况,包括 UBC,并且不要超过总可用 iOS 内存池的 50%。

那么(你可能会问)——苹果批准的解决这个问题的方法是什么?嗯,没有。我怎么知道呢?因为我在 WWDC 2012 上与一个实验室的 Core iOS 总监进行了长达半小时的讨论(其他人不知道我在说什么)。最后,在我解释了上述启发之后,他直接告诉我,这个解决方案可能是他能想到的最好的解决方案。如果没有 API 直接监控 UBC,您只能估算其使用情况并进行相应调整。

但你说,我正在使用 NSCache - 为什么系统不考虑AVPlayer记忆在那里?原因无疑是UBC——AVPlayer实例本身可能只消耗几千K的内存——它是iOS不考虑的视频的打开文件。

可能的解决方案

1)如果你可以将视频直接加载到 NSData 对象中,并将其保留在 NSCache 中,那么你很可能完全避免上面提到的 UBC 问题。 [我对 AV 系统了解不够,不知道你是否可以做到这一点。] 在这种情况下,系统should能够在需要时清除内存。

2) 继续使用原来的代码,但添加内存管理。也就是说,当您创建 AVPlayer 实例时,您需要考虑视频的大小(以字节为单位),并保留所有这些内存的运行记录。当您接近设备可用内存总量的 50% 时,开始清除旧的 AVPlayer。

Code

为了完整起见,我提供了相关代码相片卷轴网络 https://github.com/dhoerl/PhotoScrollerNetwork以下。如果您想了解更多详细信息,可以仔细阅读该项目 - 然而它非常复杂,因此需要花费一些时间(它对大量图像进行动态 JPEG 解码,并在解码过程中将图块写入文件系统)。

// Data Structure
typedef struct {
    size_t freeMemory;
    size_t usedMemory;
    size_t totlMemory;
    size_t resident_size;
    size_t virtual_size;
} freeMemory;

在您的应用程序的早期:

// ubc_threshold_ratio defaults to 0.5f
// Take a big chunk of either free memory or all memory
freeMemory fm       = [self freeMemory:@"Initialize"];
float freeThresh    = (float)fm.freeMemory*ubc_threshold_ratio;
float totalThresh   = (float)fm.totlMemory*ubc_threshold_ratio;
size_t ubc_threshold = lrintf(MAX(freeThresh, totalThresh));
size_t ubc_usage = 0;

// Method on some class to monitor the memory pool
- (freeMemory)freeMemory:(NSString *)msg
{
    // http://stackoverflow.com/questions/5012886
    mach_port_t host_port;
    mach_msg_type_number_t host_size;
    vm_size_t pagesize;
    freeMemory fm = { 0, 0, 0, 0, 0 };

    host_port = mach_host_self();
    host_size = sizeof(vm_statistics_data_t) / sizeof(integer_t);
    host_page_size(host_port, &pagesize);        

    vm_statistics_data_t vm_stat;

    if (host_statistics(host_port, HOST_VM_INFO, (host_info_t)&vm_stat, &host_size) != KERN_SUCCESS) {
        LOG(@"Failed to fetch vm statistics");
    } else {
        /* Stats in bytes */ 
        natural_t mem_used = (vm_stat.active_count +
                              vm_stat.inactive_count +
                              vm_stat.wire_count) * pagesize;
        natural_t mem_free = vm_stat.free_count * pagesize;
        natural_t mem_total = mem_used + mem_free;

        fm.freeMemory = (size_t)mem_free;
        fm.usedMemory = (size_t)mem_used;
        fm.totlMemory = (size_t)mem_total;

        struct task_basic_info info;
        if(dump_memory_usage(&info)) {
            fm.resident_size = (size_t)info.resident_size;
            fm.virtual_size = (size_t)info.virtual_size;
        }

#if MEMORY_DEBUGGING == 1
        LOG(@"%@:   "
            "total: %u "
            "used: %u "
            "FREE: %u "
            "  [resident=%u virtual=%u]", 
            msg, 
            (unsigned int)mem_total, 
            (unsigned int)mem_used, 
            (unsigned int)mem_free, 
            (unsigned int)fm.resident_size, 
            (unsigned int)fm.virtual_size
        );
#endif
    }
    return fm;
}

当您打开视频时,将尺寸添加到ubc_usage,当你关闭一个时减少它。当您想打开新视频时,请测试 ubc_usageubc_threadhold,它超出了您必须首先关闭某些内容的值。

PS:您可以尝试在其他时间调用该 freeMemory 方法,然后看看,但就我而言,当文件打开时它几乎没有变化 - 系统似乎将整个 UBC 视为“免费”,因为它could如果需要的话清除它(我猜)。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

我的 AVPlayer 的内存在哪里?如何取回它? 的相关文章

随机推荐