我是靠谱客的博主 健康指甲油,最近开发中收集的这篇文章主要介绍WKWebView 线程终止的原因——之 OOM 的数值抽离 WebKit 的计算方法直接引用 WebKit 的基础模块运行结果参考,觉得挺不错的,现在分享给大家,希望可以做个参考。

概述

上篇文章介绍了WKWebView 线程终止的原因——之 OOM 的控制逻辑,那 iOS 的最大可用内存到底是多少呢?我们可不可以将 WebKit 中的计算逻辑拿出来运行一下呢?

最终的实现过程可以查看 GitHub 上的 WKWebViewMemory。

抽离 WebKit 的计算方法

我们可以尝试将WKWebView 线程终止的原因——之 OOM 的控制逻辑中找的的对应方法,放到一个 app 程序当中,来获取对应的数值。

整体的方法整理在如下:

#include "MemoryPressure.hpp"
#include <iostream>
#include <mutex>
#include <array>
#include <mutex>
#import <algorithm>
#import <dispatch/dispatch.h>
#import <mach/host_info.h>
#import <mach/mach.h>
#import <mach/mach_error.h>
#import <math.h>
#define MEMORYSTATUS_CMD_GET_MEMLIMIT_PROPERTIES 8
static constexpr size_t kB = 1024;
static constexpr size_t MB = kB * kB;
static constexpr size_t GB = kB * kB * kB;
static constexpr size_t availableMemoryGuess = 512 * MB;
#if __has_include(<System/sys/kern_memorystatus.h>)
extern "C" {
#include <System/sys/kern_memorystatus.h>
}
#else
extern "C" {
using namespace std;
typedef struct memorystatus_memlimit_properties {
int32_t memlimit_active;
/* jetsam memory limit (in MB) when process is active */
uint32_t memlimit_active_attr;
int32_t memlimit_inactive;
/* jetsam memory limit (in MB) when process is inactive */
uint32_t memlimit_inactive_attr;
} memorystatus_memlimit_properties_t;
#define MEMORYSTATUS_CMD_GET_MEMLIMIT_PROPERTIES 8
#define MEMORYSTATUS_CMD_SET_PROCESS_IS_FREEZABLE 18
#define MEMORYSTATUS_CMD_GET_PROCESS_IS_FREEZABLE 19
}
#endif // __has_include(<System/sys/kern_memorystatus.h>)
extern "C" {
int memorystatus_control(uint32_t command, int32_t pid, uint32_t flags, void *buffer, size_t buffersize);
}
size_t MemoryPressure::jetsamLimit()
{
memorystatus_memlimit_properties_t properties;
pid_t pid = getpid();
if (memorystatus_control(MEMORYSTATUS_CMD_GET_MEMLIMIT_PROPERTIES, pid, 0, &properties, sizeof(properties)))
return 840 * MB;
if (properties.memlimit_active < 0)
return std::numeric_limits<size_t>::max();
return static_cast<size_t>(properties.memlimit_active) * MB;
}
size_t MemoryPressure::memorySizeAccordingToKernel()
{
host_basic_info_data_t hostInfo;
mach_port_t host = mach_host_self();
mach_msg_type_number_t count = HOST_BASIC_INFO_COUNT;
kern_return_t r = host_info(host, HOST_BASIC_INFO, (host_info_t)&hostInfo, &count);
mach_port_deallocate(mach_task_self(), host);
if (r != KERN_SUCCESS)
return availableMemoryGuess;
if (hostInfo.max_mem > std::numeric_limits<size_t>::max())
return std::numeric_limits<size_t>::max();
return static_cast<size_t>(hostInfo.max_mem);
}
size_t MemoryPressure::computeAvailableMemory()
{
size_t memorySize = memorySizeAccordingToKernel();
size_t sizeJetsamLimit = jetsamLimit();
cout << "jetsamLimit:" << sizeJetsamLimit / 1024 / 1024 << "MBn";
cout << "memorySize:" << memorySize / 1024 / 1024 << "MBn";
size_t sizeAccordingToKernel = std::min(memorySize, sizeJetsamLimit);
size_t multiple = 128 * MB;
// Round up the memory size to a multiple of 128MB because max_mem may not be exactly 512MB
// (for example) and we have code that depends on those boundaries.
sizeAccordingToKernel = ((sizeAccordingToKernel + multiple - 1) / multiple) * multiple;
cout << "sizeAccordingToKernel:" << sizeAccordingToKernel / 1024 / 1024 << "MBn";
return sizeAccordingToKernel;
}
size_t MemoryPressure::availableMemory()
{
static size_t availableMemory;
static std::once_flag onceFlag;
std::call_once(onceFlag, [this] {
availableMemory = computeAvailableMemory();
});
return availableMemory;
}
size_t MemoryPressure::computeRAMSize()
{
return availableMemory();
}
size_t MemoryPressure::ramSize()
{
static size_t ramSize;
static std::once_flag onceFlag;
std::call_once(onceFlag, [this] {
ramSize = computeRAMSize();
});
return ramSize;
}
size_t MemoryPressure::thresholdForMemoryKillOfActiveProcess(unsigned tabCount)
{
size_t ramSizeV = ramSize();
cout << "ramSize:" << ramSizeV / 1024 / 1024 << "MBn";
size_t baseThreshold = ramSizeV > 16 * GB ? 15 * GB : 7 * GB;
return baseThreshold + tabCount * GB;
}
size_t MemoryPressure::thresholdForMemoryKillOfInactiveProcess(unsigned tabCount)
{
//#if CPU(X86_64) || CPU(ARM64)
size_t baseThreshold = 3 * GB + tabCount * GB;
//#else
//
size_t baseThreshold = tabCount > 1 ? 3 * GB : 2 * GB;
//#endif
return std::min(baseThreshold, static_cast<size_t>(ramSize() * 0.9));
}

上面方法中的具体作用,可以查看WKWebView 线程终止的原因——之 OOM 的控制逻辑。

Swift 并不能直接调用 C++ 的方法,所以,我们需要使用 Object-C 进行封装:

#import "MemoryPressureWrapper.h"
#import "MemoryPressure.hpp"
@implementation MemoryPressureWrapper
+ (size_t)thresholdForMemoryKillOfActiveProcess {
MemoryPressure cpp;
return cpp.thresholdForMemoryKillOfActiveProcess(1);
}
+ (size_t)thresholdForMemoryKillOfInactiveProcess {
MemoryPressure cpp;
return cpp.thresholdForMemoryKillOfInactiveProcess(1);
}
@end

另外:如果 Object-C 调用 C++ 代码时,要将创建的 .m 文件后缀改成 .mm ,告诉 XCode 编译该文件时要用到 C++ 代码。

直接引用 WebKit 的基础模块

在 WebKit 中,内存相关的方法在 WTFbmalloc 模块中。我们可以下载下来源码,然后创建一个 APP 来引用。步骤如下:

  1. 下载 WebKit 源码,找到 Source/WTFSource/bmalloc 模块,和 Tools/ccache 文件。
  2. 新建一个 WorkSpace:WKWebViewMemory,再新建、添加一个 iOS Project:WKWebViewMemoryApp。并将步骤 1 中的 Source/WTFSource/bmalloc 模块添加到 WorkSpace 中,
  3. 在 WKWebViewMemoryApp 的 TARGETS 的 Build Settings 中,找到 Header Search Paths,添加 $(BUILT_PRODUCTS_DIR)/usr/local/include$(DSTROOT)/usr/local/include$(inherited)
  4. 因为 WTF中的计算方法为 private 的,为了能在 app 中进行访问,需要修改为 public。

最终,就可以通过下面的方式进行获取了:

#import "WTFWrapper.h"
#import <wtf/MemoryPressureHandler.h>
// 这个文件必须名称为 .mm 类型,否自会编译错误
@implementation WTFWrapper
+ (size_t)thresholdForMemoryKillOfActiveProcess {
return WTF::thresholdForMemoryKillOfActiveProcess(1);
}
+ (size_t)thresholdForMemoryKillOfInactiveProcess {
return WTF::thresholdForMemoryKillOfInactiveProcess(1);
}
@end

运行结果

在 iPhoneXS 上运行的结果为:

jetsamLimit 的值为:840MB
内存大小(memorySize)为:3778MB
ramSize为:896MB
激活状态下(ActiveProcess)的最大可用内存为:8G
非激活状态下(InactiveProcess)的最大可用内存为:806M

奇怪的结果:在最大内存为 3778MB(不到 4G)的手机上,最大可用内存居然为 8G。

为什么?难道计算错了?难道内存没有限制?

我们来重新看一下获取最大可用内存的方法:

std::optional<size_t> MemoryPressureHandler::thresholdForMemoryKill()
{
if (m_configuration.killThresholdFraction)
return m_configuration.baseThreshold * (*m_configuration.killThresholdFraction);
switch (m_processState) {
case WebsamProcessState::Inactive:
return thresholdForMemoryKillOfInactiveProcess(m_pageCount);
case WebsamProcessState::Active:
return thresholdForMemoryKillOfActiveProcess(m_pageCount);
}
return std::nullopt;
}

如果配置当中设置了 killThresholdFraction,则会通过 m_configuration.baseThreshold * (*m_configuration.killThresholdFraction); 进行计算。

我怀疑是抽离出来的方法,没有设置 killThresholdFraction,而在 iOS 系统中,在初始化 WKWebView 时,会设置此值,来返回一个合理的数值。

那在 iOS 系统中,thresholdForMemoryKill() 究竟会返回多少呢?可能只能通过 WKWebView 的源码进行获取了。

如果想获取最终可以运行的项目,可以查看 GitHub 上的 WKWebViewMemory。

参考

  • iOS开发 - 在 Swift 中去调用 C/C++ 代码

最后

以上就是健康指甲油为你收集整理的WKWebView 线程终止的原因——之 OOM 的数值抽离 WebKit 的计算方法直接引用 WebKit 的基础模块运行结果参考的全部内容,希望文章能够帮你解决WKWebView 线程终止的原因——之 OOM 的数值抽离 WebKit 的计算方法直接引用 WebKit 的基础模块运行结果参考所遇到的程序开发问题。

如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(59)

评论列表共有 0 条评论

立即
投稿
返回
顶部