其他测试框架 [腾讯 TMQ] win32 应用程序性能测试-内存篇

匿名 · 2017年01月17日 · 869 次阅读

本文主要讲述 windows 平台下应用程序性能测试的内存相关的知识,通过本文了解内存基本原理和分析内存占用问题。

## 一、内存是什么?

###1.内存分为物理内存和虚拟内存
物理内存指通过物理内存条而获得的内存空间,虚拟内存是计算机系统内存管理的一种技术。它使得应用程序认为它拥有连续的可用的内存(一个连续完整的地址空间)。

####2.两者都有系统约定的最大值
进程占用的内存一般是指物理内存,其中操作系统为每个进程的工作集定义了一个最小和最大工作集。每个进程的 工作集有最小工作集(20-50M)最大是 45-345M

虚拟内存:每个 32 位的进程操作系统为其分配最大 4G 的线性虚拟地址空间,地址是从 0X00000000~0XFFFFFFFF 其中低的 2G 留给进程,高的 2G 留给系统。

####3.查看物理内存和虚拟内存
1) 电脑配置

例如我的电脑配置是 :如下,安装的内存是 16G。查看资源监视器里显示,所以平时所说的内存占用是指物理内存。

2) 内存在资源管理器概念

例如这里进程: 物理内存(工作集)=可共享 + 专用工作集:是私人工作集中的内存数量与进程正在使用且可以由其他进程共享的内存数量的总和。
专用工作集:- 是工作集的一个子集,专用工作集专门描述了某个进程正在使用的且无法与其他进程共享的内存数量。
提交大小:是为某进程使用而保留的虚拟内存的数量。

3) 虚拟内存查看
虚拟内存,用 VMMAP 工具查看。如下图

具体含义可以去网上搜索。其中虚拟内存有三种状态:

1)自由(free)是指内存还未使用。

2)当申请内存使用 VirtualAlloc 传入 MEM_RESERVE 执行预留 (reserved) 操作。

3)当真正访问内存的数据才执行提交 (committed),传入 MEM_COMMINT 参数。

## 二、系统如何管理内存

###1.系统使用 PFN 数据库
系统使用 PFN(Page Frame Number)数据库来存储物理内存。分几类 page list 来存储,如下面的图

Freelist :可用来分配,但是含有脏数据

modified:以前属于某个进程,但是需要写入到后备存储,为备后面恢复

standby :跟进程关联,但是不计算在工作集里

zero:置 0 后用来分配

可缓存内存是指在 smodified 和 standby 上的,可用的内存是指在 zero \free\standby lists,如果这个值小于 800M 的话。windows 就会消减工作集,会导致整体性能变差。

###2/操作过程
1)windows 启动时,所有的内存全部是在 free page list.当进程请求内存时,(我理解为发生一次错误,从 zero page file)。进程退出,则会将内存还到 free page list。

2)操作系统有两个线程会执行一些操作,一个是 zero page thread,当要将 free page 移动到 zeroed page 时 进行置 0 free page

3)第二个是 modified page writer,将 modified page list 移动到 standby page list 时,进行第一次写出任何数据

页错误

1) 什么是页错误

访问数据时,进行虚拟地址映射到物理地址过程中,硬件检查页表时,发现所访问的页面不在内存,就产生异常 -- 缺页异常,这个缺页异常就叫做页错误。操作系统会执行缺页异常处理程序:获得磁盘的地址、启动磁盘、将该页调入内存。如下图,是<<微软核心编程>>里例子,当访问的数据不在内存中就会发生一次 fault,其中当访问 page 不在 modified 和 standy 里,则会发生 HardPageFault。HardPageFault 需要去系统的 pagefile.sys 里查找,这个查找过程会产生大量的 IO 操作,影响性能。

2) 页错误的类型

Transition:是指访问的 page 是指在 modify 或者 stadby page list 上 DemandZero:进程请求内存是,调用是 zero page listHardPageFault:访问的 page 不在工作集里,需要去磁盘 pagefile 去查找 copy on write:写入 copy-on-write page。例如你要 hook 一个 kernel 上的函数,就是操作 kernel 上的 page,需要先拷贝一份,这样不会影响其它进程使用 kernel 上的函数,这个操作就会发生一次 copy on write 错误

内存的分配 API

1)利用 HeapAlloc 方法或 C/C++ 运行时中的 malloc 或 new 来进行堆内存分配。

2)利用 VirtualAlloc 方法从系统中直接分配内存。VirtualAlloc 是 Windows 提供的 API,通常用来分配大块的内存

3)由内核通过 CreateFile, CreateEvent, or CreateThread 等 Kernel32 APIs ,来代表应用程序进行处理

4)利用 User32 和 Gdi32 APIs 来处理 GDI 和 USER 。(默认情况下,每一个线程都有 10,000 处理( 10,000 handles )配额)

## 三、如何优化内存占用高的问题

###1.刷内存

刷内存 SetProcessWorkingSetSize

1) 原理

函数用来设置应用程序最小和最大的运行空间,只会保留需要的内存,例如我们的部分 exe 里是有刷内存,这里设置的最小 8M,最大 14M

2) 缺点

刷内存只是将可能暂时不需要工作集 swap 出去,如果业务又再需要,需从虚拟内存的 pagefile 里调用过来,这个过程反而降低系统性能,所以不推荐使用

###2.减少页错误
这里推荐的操作是预处理,减少随机 IO 等。

###3.查找占用不合理和分配不合理的地方

1) 例子:某个 dll 申请内存不合理

分析过程 1)抓取对应 exe 启动过程中 VirtualAlloc。

2)查看启动过程中过程中 VirtualAlloc 分配的类型是 AIFO【AIFO 是指在此阶段分配但未释放,这种就是可疑的分配点】。发现其中有一段每次分配 495K

3)查看对应的分配堆栈如下,发现是调用

CreateToolhelp32Snapshot 方法,引起这次分配。查看系统 API。CreateToolhelp32Snapshot 可以通过获取进程信息为指定的进程、进程使用的堆 [HEAP]、模块 [MODULE]、线程建立一个快照。

4)查看源码,发现这里是每隔 3S,去查询系统所有进程的一个快照,所以每次大小都有接近 500K。而且场景的触发频率非常高,每隔 1S 检测一次,后面跟开发核实,这里的逻辑不应如此设计

5)通过这个问题,我们可以去系统检索一个分配内存大的函数,例如 CreateToolhelp32Snapshot,是不是所有触发逻辑合理,轮询调用是非常不合理,应该要规避,而且这个代码是常驻的。这个留在后面形成相应的规则

###2.例子:是内存泄露还是?

例子:某个版本的资源挂机突然 VM 增加,是内存泄露还是?1)现象:如下图一个内部版本,在某个长时间挂机,突然出现在 1 个小时和 4 个小时后,内存增长 10M 的样子。时间跨度长,如何获取增长时的内存分配堆栈?

方法一、在内存增长时,trace。缺点:无法准确捕获这个时刻

方法二、看图,应该在 1 个半小时能出现,一直 trace 这个过程的 VirtualAlloc 和 heapAlloc。因 xperf 开启 heapalloc 消耗太大,只能针对指定进程进行 trace

2)复现过程:

安装同样的版本,发现本地也会出现 VM 从 32M 最后涨到 50M 的情况。

设计 trace 的日志

Xperf -on PROC_THREAD+LOADER+CSWITCH+DISPATCHER+VIRT_ALLOC -stackwalkCSwitch+ReadyThread

xperf -start HeapSession -heap -Pids 18908 -BufferSize 1024 -MinBuffers 128 -MaxBuffers 128 -stackwalkHeapAlloc+HeapRealloc

#### 注:第二条是开启 heapsession 来 trace 指定进程的 heapalloc。整个过程 etl 太大,全部需要输出到文件模式

分析 trace 过程的分配。

如下图,查看到这个过程有几个分配大的点。其中有一个 1M 到 4M,增长 3M 的情况和对应的堆栈。

其中 0xfd10000 这个对象分配 3.176M,这个堆栈全部都是系统的 API

对应 CPU 调度图来查看是什么原因导致这个分配,这里表现是一个 TPKTT.dll

查看当时的内存 dump,查看 0xfd10000 对象,调用的堆栈是文件监控里

结合上面,怀疑是文件监控导致的,但是跟开发确认文件监控很久未变更,而且文件监控是底层逻辑,所有业务会触及。另一方面,查看 TPKTT.dll 相关的信息,他们的里面的逻辑没有泄露点
是不是挂机的现象和我这不一样,同样在挂机的机器上抓 trace 日志,最后问题点是一样的
再次分析内存分配的堆栈,向前查看,发现调用文件监控逻辑前有一个 Loadlibrary 的操作,而且 TPKTT.dll 的大小正好也是 3M 多

开发再次排查他们的逻辑,原来在静默 1 个小时后,会触发引擎的 TPK 的逻辑,这里,概会增加 10 到 13M 的样子,所以 RTP VM 增加属于业务的需要,引擎库加载需要内存。这个逻辑触发在 70 分钟到 90 分钟之间。因为我们的挂机以前只挂 1 个小时,这次是因为验证其它问题,所以挂机时间长出现这个问题。经过 2 天的定位,终于确认这次增长。

## 四、结尾

性能里调优内存涉及的点比较多,上面几个例子只是部分。建议平时先建设基础基准数据,有业务增加及时定位。

附我们内存优化的一些方向:

参考文章:

https://blogs.msdn.microsoft.com/tims/2010/10/29/pdc10-mysteries-of-windows-memory-management-revealed-part-two/

【TMQ 新书专栏】https://weidian.com/?userid=984448577

原文链接:http://tmq.qq.com/2017/01/application_test_memory/

关注我们的微信公众号查看完整内容哦~~~~

想知道更多测试相关干货
请关注我们的微信公众号:腾讯移动品质中心 TMQ
二维码:
这里写图片描述

版权声明:腾讯 TMQ 拥有内容的全部版权,任何人或单位对本贴内容进行复制、转载时请申明原创腾讯 tmq,否则将追究法律责任。

暂无回复。
需要 登录 后方可回复, 如果你还没有账号请点击这里 注册