96SEO 2026-02-19 19:02 0
前言使用效果使用apiJNI的动态注册native方法动态注册

hook的实现android_dlopen_ext和dl_iterate_phdr
Android的native泄漏怎么检测下面通过研究开源项目KOOM来一探究竟。
KOOM文档里面写着native的实现是使用了Google官方给出的libmemunreachable
参考地址https://android.googlesource.com/platform/system/memory/libmemunreachable//master/README.md
LeakMonitor.INSTANCE.start()LeakMonitor.INSTANCE.checkLeaks()LeakMonitor.INSTANCE.stop()简单行代码即可完成但是别忘了我们的目的是研究他是怎么实现的。
native方法的定义简单看方法名可以大概知道有安装监控器、卸载监控器、设置阈值和获取存储泄漏数据的容器。
但是一眼看不出来nativeGetAllocIndex的作用。
nativeInstallMonitor(selectedList:
nativeUninstallMonitor()JvmStaticprivate
nativeSetMonitorThreshold(size:
nativeGetLeakAllocs(leakRecordMap:
([Ljava/lang/String;[Ljava/lang/String;Z)Z,reinterpret_castvoid
*(InstallMonitor)},{nativeUninstallMonitor,
*(UninstallMonitor)},{nativeSetMonitorThreshold,
*(SetMonitorThreshold)},{nativeGetAllocIndex,
*(GetAllocIndex)},{nativeGetLeakAllocs,
(Ljava/util/Map;)V,reinterpret_castvoid
(vm-GetEnv(reinterpret_castvoid
leak_monitor;FIND_CLASS(leak_monitor,
(env-RegisterNatives(leak_monitor,
kLeakMonitorMethods,NELEM(kLeakMonitorMethods))
}这里进行了JNI函数的动态注册至于动态注册的优点就是可以提高一点代码性能无需通过重复查表来链接对应的native方法。
};我们找到了对应的native方法先来看看InstallMonitor方法的实现
leak_record;FIND_CLASS(leak_record,
kLeakRecordFullyName);g_leak_record.global_ref
reinterpret_castjclass(env-NewGlobalRef(leak_record));if
false;}GET_METHOD_ID(g_leak_record.construct_method,
init,(JILjava/lang/String;[Lcom/kwai/koom/nativeoom/leakmonitor/FrameInfo;)V);jclass
frame_info;FIND_CLASS(frame_info,
kFrameInfoFullyName);g_frame_info.global_ref
reinterpret_castjclass(env-NewGlobalRef(frame_info));if
false;}GET_METHOD_ID(g_frame_info.construct_method,
init,(JLjava/lang/String;)V);g_enable_local_symbolic
env-GetArrayLength(jobject_array);if
reinterpret_castjstring(env-GetObjectArrayElement(jobject_array,
nullptr);ret.emplace_back(data);env-ReleaseStringUTFChars(str,
std::move(ret);};std::vectorstd::string
selected_array);std::vectorstd::string
LeakMonitor::GetInstance().Install(selected_so,
首先定义了一个jclass变量leak_record和frame_info并通过FIND_CLASS宏查找Java类并创建全局引用。
使用GET_METHOD_ID宏获取leak_record和frame_info类的构造方法ID用于后续实例化对象。
定义了一个lambda函数array_to_vector该函数接受一个JNIEnv指针和一个jobjectArray对象作为参数将其转换为C的字符串向量。
LeakMonitor::Install(std::vectorstd::string
*selected_list,std::vectorstd::string
{KCHECK(!has_install_monitor_);//
std::make_uniqueMemoryAnalyzer();if
{^/data/.*\\.so$};std::vectorconst
{.*/libkoom-native.so$,.*/libxhook_lib.so$};if
listregister_pattern.clear();for
{register_pattern.push_back(^/data/.*/
.so$);}}std::vectorstd::pairconst
*(WRAP(malloc))),std::make_pair(realloc,
*(WRAP(realloc))),std::make_pair(calloc,
*(WRAP(calloc))),std::make_pair(memalign,
*(WRAP(memalign))),std::make_pair(posix_memalign,reinterpret_castvoid
*(WRAP(posix_memalign))),std::make_pair(free,
(HookHelper::HookMethods(register_pattern,
true;}HookHelper::UnHookMethods();live_alloc_records_.Clear();memory_analyzer_.reset(nullptr);ALOGE(%s
}这个函数前面通过正则表达式定义了需要hook和不需要hook的动态库。
后续就是把需要hook的系统内存管理函数put到容器里面接着传给HookHelper去实现hook。
HookHelper::HookMethods(std::vectorconst
register_pattern,std::vectorconst
ignore_pattern,std::vectorstd::pairconst
std::move(register_pattern);ignore_pattern_
std::move(ignore_pattern);methods_
std::move(methods);DlopenCb::GetInstance().AddCallback(Callback);return
HookHelper::Callback(std::setstd::string
{pthread_mutex_lock(DlopenCb::hook_mutex);xhook_clear();for
(xhook_register(pattern.c_str(),
pattern.c_str(),method.first.c_str());pthread_mutex_unlock(DlopenCb::hook_mutex);return
pattern.c_str(),method.first.c_str());pthread_mutex_unlock(DlopenCb::hook_mutex);return
xhook_refresh(0);pthread_mutex_unlock(DlopenCb::hook_mutex);return
static_caststd::pairstd::setstd::string
函数是一个回调函数它用于迭代动态链接器的程序头部信息。
它的功能如下
//一开始输出日志表示刷新操作开始。
XH_LOG_INFO(Refresh
集合用于存储新增的共享库。
std::setstd::string
addLibs;pthread_mutex_lock(add_lib_mutex);//获取
addLibs);dl_iterate_phdr(Callback,
callbackData);pthread_mutex_unlock(add_lib_mutex);//如果
{pthread_mutex_lock(hook_mutex);xhook_clear();//根据调试模式进行设置。
if
{xhook_enable_sigsegv_protection(0);xhook_enable_debug(1);}
{xhook_enable_sigsegv_protection(1);}for
lib.c_str();xhook_register(lib_ctr,
//输出日志表示新增的共享库已添加。
XH_LOG_INFO(Refresh
钩子。
xhook_refresh(0);pthread_mutex_unlock(hook_mutex);//
hooked);pthread_mutex_lock(callback_mutex);//对回调函数进行通知传递新增的共享库信息。
for
loadLibName);}pthread_mutex_unlock(callback_mutex);}
集合为空则输出日志表示没有发现新增的共享库。
XH_LOG_INFO(Refresh
dl_iterate_phdr但这两个函数的实现只是简单地返回了
android_dlopen_ext和dl_iterate_phdr
字符串。
__flags这是一个整数用于指定加载共享库的选项标志。
__info这是一个指向
结构体的指针该结构体用于传递扩展加载选项的详细信息。
如果不需要传递额外信息可以传入
类型的指针该指针通常用于表示加载的共享库的句柄或者标识符。
在这个声明中函数总是返回
的函数该函数是用来迭代动态链接器的程序头部信息的。
这个函数通常在操作系统中用于获取运行时链接器Runtime
这是一个函数指针指向一个函数该函数用于处理迭代过程中获取的动态链接库的信息。
它接受三个参数
这是一个结构体指针用于存储动态链接库的程序头部信息。
size_t:
WRAP(function)(__VA_ARGS__)这里通过宏定义了HOOK函数把前面的系统内存分配函数进行了hook这里
*(WRAP(malloc))),std::make_pair(realloc,
*(WRAP(realloc))),std::make_pair(calloc,
*(WRAP(calloc))),std::make_pair(memalign,
*(WRAP(memalign))),std::make_pair(posix_memalign,reinterpret_castvoid
*(WRAP(posix_memalign))),std::make_pair(free,
*(WRAP(free)))};先看下hook了malloc的实现代码
malloc(size);LeakMonitor::GetInstance().OnMonitor(reinterpret_castintptr_t(result),size);CLEAR_MEMORY(result,
LeakMonitor::OnMonitor(uintptr_t
alloc_threshold_.load(std::memory_order_relaxed))
{return;}RegisterAlloc(address,
}这里判断了一下阈值加入达到阈值则执行RegisterAlloc
LeakMonitor::RegisterAlloc(uintptr_t
kMaxBacktraceSize);};thread_local
std::make_sharedAllocRecord();alloc_record-address
CONFUSE(address);alloc_record-size
alloc_index_;memcpy(alloc_record-thread_name,
kMaxThreadNameLen);unwind_backtrace(alloc_record-backtrace,
(alloc_record-num_backtraces));live_alloc_records_.Put(CONFUSE(address),
StackTrace::FastUnwind(uintptr_t
{pthread_once(once_control_tls,
reinterpret_castuintptr_t(__builtin_frame_address(0));auto
reinterpret_castuintptr_t(ss.ss_sp)
GetAdjustPC(frame-return_addr);}num_frames;if
}这里记录了函数调用的回溯栈相关数据包括起始地址和大小。
结合前面的方法可以知道就是获取调用栈的信息然后put到live_alloc_records_里面。
分析到这里我们回头看下外面调用的nativeGetLeakAllocs方法。
分析到这里我们可以知道它会执行到这里
std::vectorstd::shared_ptrAllocRecord
{KCHECK(has_install_monitor_);auto
memory_analyzer_-CollectUnreachableMem();std::vectorstd::shared_ptrAllocRecord
live_allocs;std::vectorstd::shared_ptrAllocRecord
{live_allocs.push_back(alloc_info);};live_alloc_records_.Dump(collect_func);auto
[](decltype(unreachable_allocs)::value_type
unreachable,std::shared_ptrAllocRecord
{leak_allocs.push_back(live);//
firstUnregisterAlloc(CONFUSE(live-address));}}}return
}这里分为live_allocs和leak_allocs通过CollectUnreachableMem来获取不可达的内存信息并保存到leak_allocs中。
到这里核心的实现就已经走读完毕。
后续的代码就是把相关的泄漏内存信息的进行一系列业务处理和打印了。
但是思路和原理已经跃然纸上了不是吗
总体来说KOOM监控native层内存泄漏的实现原理就是通过hook。
hook住系统内存分配的api然后再api的hook方法里面调用Google的memunreachable模块对内存进行可达性分析从而获取泄漏内存的起始地址和空间大小。
作为专业的SEO优化服务提供商,我们致力于通过科学、系统的搜索引擎优化策略,帮助企业在百度、Google等搜索引擎中获得更高的排名和流量。我们的服务涵盖网站结构优化、内容优化、技术SEO和链接建设等多个维度。
| 服务项目 | 基础套餐 | 标准套餐 | 高级定制 |
|---|---|---|---|
| 关键词优化数量 | 10-20个核心词 | 30-50个核心词+长尾词 | 80-150个全方位覆盖 |
| 内容优化 | 基础页面优化 | 全站内容优化+每月5篇原创 | 个性化内容策略+每月15篇原创 |
| 技术SEO | 基本技术检查 | 全面技术优化+移动适配 | 深度技术重构+性能优化 |
| 外链建设 | 每月5-10条 | 每月20-30条高质量外链 | 每月50+条多渠道外链 |
| 数据报告 | 月度基础报告 | 双周详细报告+分析 | 每周深度报告+策略调整 |
| 效果保障 | 3-6个月见效 | 2-4个月见效 | 1-3个月快速见效 |
我们的SEO优化服务遵循科学严谨的流程,确保每一步都基于数据分析和行业最佳实践:
全面检测网站技术问题、内容质量、竞争对手情况,制定个性化优化方案。
基于用户搜索意图和商业目标,制定全面的关键词矩阵和布局策略。
解决网站技术问题,优化网站结构,提升页面速度和移动端体验。
创作高质量原创内容,优化现有页面,建立内容更新机制。
获取高质量外部链接,建立品牌在线影响力,提升网站权威度。
持续监控排名、流量和转化数据,根据效果调整优化策略。
基于我们服务的客户数据统计,平均优化效果如下:
我们坚信,真正的SEO优化不仅仅是追求排名,而是通过提供优质内容、优化用户体验、建立网站权威,最终实现可持续的业务增长。我们的目标是与客户建立长期合作关系,共同成长。
Demand feedback