Ios 我的AVPlayer';这是我的记忆,我怎样才能找回它?
我正在使用Ios 我的AVPlayer';这是我的记忆,我怎样才能找回它?,ios,video,avplayer,didreceivememorywarning,Ios,Video,Avplayer,Didreceivememorywarning,我正在使用AVPlayer同时播放大量视频。为了减少加载时间,我将相应的视图存储在NSCache中 在达到一定数量的视频之前,这一切都是正常的,视频从中停止播放,甚至停止出现 没有错误、日志或内存警告。特别是,我正在收听UIApplicationIDReceiveMemoryWarningNotification以清除缓存,但从未收到此消息 如果删除缓存,所有视频播放都会以性能下降为代价 这让我怀疑AVPlayer正在使用来自不同进程的内存(哪一个?)。当记忆达到一定限度时,新玩家就会停止工作
AVPlayer
同时播放大量视频。为了减少加载时间,我将相应的视图存储在NSCache
中
在达到一定数量的视频之前,这一切都是正常的,视频从中停止播放,甚至停止出现
没有错误、日志或内存警告。特别是,我正在收听UIApplicationIDReceiveMemoryWarningNotification
以清除缓存,但从未收到此消息
如果删除缓存,所有视频播放都会以性能下降为代价
这让我怀疑AVPlayer
正在使用来自不同进程的内存(哪一个?)。当记忆达到一定限度时,新玩家就会停止工作
这是正确的吗
如果是这样的话,是否有办法在达到此神奇极限时得到通知,以采取适当措施(例如清除缓存)确保播放其他媒体?好消息和坏消息-好消息是您可能会解决问题,坏消息是它需要工作,而且有些复杂 根本问题 你没有提前得到通知的原因是,iOS没有发现你的应用程序已经超出了内存预算,直到它几乎太迟了,然后它会立即终止它。问题与iOS(和OSX)管理文件系统缓存的方式有关。通常,当打开文件时,当您读取数据时,文件数据会被传输到
统一缓冲区缓存中的缓冲区中(这是一个术语,您可以在谷歌上搜索更多信息)-从现在起,我将其称为UBC
因此,假设您有10个打开的文件,并且您已将每个文件读取到最后,但尚未关闭这些文件。所有的数据都在UBC里。现在,如果关闭文件,缓冲区将全部释放。从技术上讲,操作系统也可以清除这些缓冲区——只有当它意识到内存紧张时,它才会选择先将应用程序吹走(这可能是它这样做的正当理由)。因此,假设您的应用程序正在显示视频,并且视频的加载方式是通过文件系统,可用缓冲区的数量开始下降。在某个时刻,iOS会注意到这一点,追踪谁是你的应用的最归属者,然后砰的一声,尽快杀死你的应用
我自己在一个我支持的开源项目中遇到了这个问题。用户开始抱怨他们的项目被系统终止,就像你一样,没有任何通知。我试图监视UBC是徒劳的(OSX上有这样做的API,但iOS上没有)。最后,我找到了一个使用启发式的解决方案——监控所有内存使用情况,包括UBC,并且不要超过总可用iOS内存池的50%
那么(你可能会问)-苹果批准的解决这个问题的方法是什么?没有。我怎么知道?因为我在WWDC 2012上与Core iOS的主管在一个实验室进行了半小时的讨论(在被其他不知道我在说什么的人打乒乓球之后)。最后,在我解释了上述启发之后,他直接告诉我,这个解决方案可能是他所能想到的最好的。如果没有直接监视UBC的API,您只能近似地使用它并进行相应的调整
但是你会说,我使用的是NSCache——为什么系统不考虑那里的AVPlayer
内存?毫无疑问,这是因为UBC(一个AVPlayer
实例本身可能只消耗几千K内存)是一个开放的视频文件,而iOS没有考虑到它
可能的解决办法
1) 如果您可以将视频直接加载到NSData对象中,并将其保存在NSCache中,则很可能完全避免上述UBC问题。[我对AV系统了解不够,不知道您是否可以这样做。]在这种情况下,系统应该能够在需要时清除内存
2) 继续使用原始代码,但添加内存管理。也就是说,当您创建一个AVPlayer实例时,您需要以字节为单位说明视频的大小,并保持所有内存的运行记录。当您接近总设备可用内存的50%时,开始清除旧的AVP播放器
代码
为了完整起见,我从下面提供了相关代码。如果你想要更多的细节,你可以仔细阅读这个项目——但是它非常复杂,所以你需要花费一些时间(它会对海量图像进行动态JPEG解码,并在解码过程中将图像块写入文件系统)
在应用程序的早期:
// 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\u usage
,关闭视频时,将其减小。当你想打开一个新的视频时,根据ubc\u threadhold
测试ubc\u的使用情况,它超过了你必须先关闭的值
PS:你可以试着在其他时候调用FielMeMy方法,但是,在我的例子中,当文件被打开时,它几乎没有变化。系统似乎认为整个UBC是“免费的”,因为它可以清除它(如果需要的话)。您必须准备好缓存,以便在感觉项目占用了太多内存时将其丢弃。从:
NSCache类包含各种自动删除策略,这些策略
确保它不会占用过多的系统内存。这个
如果用户需要内存,系统将自动执行这些策略
其他应用程序。调用时,这些策略将删除某些项
从缓存中删除,最大限度地减少其内存占用
检查是否从缓存中获取nil
s,如果是,则必须重新构建对象
编辑:
还值得一提的是,建议不要将大型对象存储在NSCache
:
NSCache的逐出方法是不确定的,并且不是
记录在案。加入超级联赛不是个好主意-
// 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;
}