WebView内存使用情况
# APP的内存占用情况是如何检测出来的
# webview为何不能用app的
历史原因:
早期 UIWebView 由于与 app 共享内存空间, 会容易导致app/浏览器卡顿、白屏、甚至崩溃,甚至 input 传个大图/文件如果处理不当就直接崩;之后 WKWebView 虽然优化了 WebView 内存及管理,但仍存在很多bug,加上 iPhone 本身内存空间较小,综合起来还是容易出现卡顿、白屏、甚至崩溃问题;
WKWebview 会开辟进程(不是线程)来处理任务;
开辟的进程包括一个 content 的渲染进程和一个 networking 的网络处理进程; WKWebview 开辟的进程如果在内 存超过预算之后,使用 WKWebview 的当前 app 并不会被杀掉,最多就是 WKWebview 展示的页面变成空白页;
WKWebview 开启进程所占用的内存虽然不会影响当前 app,但是毕竟 iOS 中使用的是共享物理内存,当占用内存过大时,必定会影响到 app 的执行效率。如果 WKWebview 没有及时销毁,甚至会发生 CPU 抢占的现象,加剧原 APP 的效率问题;
但要注意,iOS 现在 App 基本会用 WKWebView,这种情况下客户端是拿不到页面(WebView)的内存信息的(因为系统共享 WebView 虚拟内存),因此像 DoraemonKit 的内存模块也是无法观察页面内存情况,这时候的方案就是获取整个设备的内存信息,通过观察设备内存变化来进行判断,缺点就是难以保证其他应用及系统的影响;
FBMemoryProfiler则可以检测所有类型的内存泄漏,原理是hook了系统的alloc和dealloc函数,跟instruments的功能类似,只不过更加轻量化,可以在APP运行时实时看到内存分配的情况,如果有对象内存泄漏,则会标红表示。
uint64_t FBMemoryProfilerResidentMemoryInBytes() { kern_return_t rval = 0; mach_port_t task = mach_task_self();
struct task_basic_info info = {0}; mach_msg_type_number_t tcnt = TASK_BASIC_INFO_COUNT; task_flavor_t flavor = TASK_BASIC_INFO;
task_info_t tptr = (task_info_t) &info;
if (tcnt > sizeof(info)) return 0;
rval = task_info(task, flavor, tptr, &tcnt); if (rval != KERN_SUCCESS) { return 0; }
return info.resident_size; }
DoraemonKit 可能使用了一些与 Instruments 类似的技术来监测应用的内存占用情况。一种常见的方法是使用 Objective-C 运行时(Objective-C Runtime)来动态地获取对象的信息,包括对象的内存占用情况。通过追踪对象的创建和释放,可以估算应用在运行时的内存占用。
- (NSInteger)useMemoryForApp{ task_vm_info_data_t vmInfo; mach_msg_type_number_t count = TASK_VM_INFO_COUNT; kern_return_t kernelReturn = task_info(mach_task_self(), TASK_VM_INFO, (task_info_t) &vmInfo, &count); if(kernelReturn == KERN_SUCCESS) { int64_t memoryUsageInByte = (int64_t) vmInfo.phys_footprint; return (NSInteger)(memoryUsageInByte/1024/1024); } else { return -1; } }
YYAppPerformanceMonitor
YYMemoryUsage memory_usage() { // 由内核提供的关于该进程的内存信息,包括虚拟内存,常驻内存,物理内存,最大常驻内存等 struct mach_task_basic_info info; mach_msg_type_number_t count = sizeof(info) / sizeof(integer_t); if (task_info(mach_task_self(), MACH_TASK_BASIC_INFO, (task_info_t)&info, &count) == KERN_SUCCESS) {
YYMemoryUsage usage;
usage.has_usage = info.resident_size / MEMORY_SIZE_PER_MB;
usage.total = [NSProcessInfo processInfo].physicalMemory / MEMORY_SIZE_PER_MB;
usage.ratio = (double)info.resident_size / (double)[NSProcessInfo processInfo].physicalMemory * 100;
return usage;
}
return (YYMemoryUsage){ 0 };
}
创建了一个定时器,每隔0.5s从内核中读取该进程的内存信息 从内核中读取该进程的内存信息 // 由内核提供的关于该进程的内存信息,包括虚拟内存,常驻内存,物理内存,最大常驻内存等
检测webvew的内存有两种
1.使用instrument检测webvew的内存 2.在webview中显示内存使用的情况
// YourViewController.h #import <UIKit/UIKit.h> #import <WebKit/WebKit.h>
@interface YourViewController : UIViewController
@property (nonatomic, strong) WKWebView *webView;
@end
// YourViewController.m #import "YourViewController.h"
@implementation YourViewController
(void)viewDidLoad { [super viewDidLoad];
WKUserContentController *userContentController = [[WKUserContentController alloc] init]; [userContentController addScriptMessageHandler:self name:@"memoryUsage"];
WKWebViewConfiguration *configuration = [[WKWebViewConfiguration alloc] init]; configuration.userContentController = userContentController;
self.webView = [[WKWebView alloc] initWithFrame:self.view.bounds configuration:configuration]; [self.view addSubview:self.webView];
NSString *htmlString = @"
"; [self.webView loadHTMLString:htmlString baseURL:nil]; }(void)userContentController:(WKUserContentController *)userContentController didReceiveScriptMessage:(WKScriptMessage *)message { if ([message.name isEqualToString:@"memoryUsage"]) { NSDictionary *memoryInfo = message.body; // 处理从 JavaScript 传递过来的内存信息 NSLog(@"Total Memory: %@, Used Memory: %@", memoryInfo[@"totalMemory"], memoryInfo[@"usedMemory"]); } }
@end