Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

获取 App 内存不准 #5

Open
ifelseboyxx opened this issue Sep 22, 2017 · 21 comments
Open

获取 App 内存不准 #5

ifelseboyxx opened this issue Sep 22, 2017 · 21 comments
Assignees
Labels

Comments

@ifelseboyxx
Copy link

- (NSUInteger)getResidentMemory
{
    struct mach_task_basic_info info;
    mach_msg_type_number_t count = MACH_TASK_BASIC_INFO_COUNT;
	
	int r = task_info(mach_task_self(), MACH_TASK_BASIC_INFO, (task_info_t)& info, & count);
	if (r == KERN_SUCCESS)
	{
		return info.resident_size;
	}
	else
	{
		return -1;
	}
}

这个拿到的值和 Xcode 上的不一致啊,多了30-40MB 左右

@aozhimin
Copy link
Owner

@ifelseboyxx 你是和 Debug Navigator 中的内存比较么,这个问题我之前也发现了,用代码统计出来的内存会比 Xcode 的内存要多几十 MB

@aozhimin aozhimin self-assigned this Sep 26, 2017
@KrisMarko
Copy link

那这个问题有解决吗?

@aozhimin
Copy link
Owner

@ifelseboyxx @PrinceMarko 我在一台越狱的设备上使用 top 命令查看进程的使用内存,发现上述代码与 top 命令拿到的 RSIZE 是一致的,我测试的数据两者都是 74 MB,至于为什么会和 Debug Gauge 的 RAM 值少几十 MB,应该是 Debug Gauge 采集到内存值少统计了一些数据,具体可以参考这篇文章
http://iosdevelopertips.com/xcode/xcode-5-debug-gauges-to-monitor-memory-and-cpu-usage.html

Speaking of which, beware of trusting the memory gauge. It’s useful, but it doesn’t tell the whole story:
– It doesn’t include OpenGL textures. If you’re using OpenGL you’re using a lot more than the gauge says (and if you leak textures you’re in big trouble ;)
– It doesn’t include frameworks too. Not normally an issue since they’re shared between apps, but if you don’t support 64bit, when your app runs on a 64bit device it forces the OS to load 32 bit versions of the frameworks. That eats a big chunk of extra RAM you’re probably not anticipating. (64bit apps use more RAM, but after taking the framework issue into account 32bit apps on a 64bit device are usually worse!)
– It doesn’t include system services your app uses. Typical example: if your app saves a photo, the media server daemon uses a big chunk of RAM to process and save the image.

至于上述代码通过 mach API 拿到进程使用的 RAM 值的这种方式,应该问题是不大,这种方式我查看其他手淘和手Q的线上代码,他们的 appUsedMemory 函数也是通过这种实现,所以可以放心使用,包括有本书《High Performance iOS Apps: Optimize Your Code for Better Apps》中的 Example 2-40 track available memory and memory used 也介绍了这种方式获取应用使用内存。

@simpleease
Copy link

用resident_size这种方式取出来的值,当不断新增malloc时(不要free掉),resident_size会达到一个最大值,然后会降下来一些;继续malloc,resident_size会继续增长,然后下降。。。 总之就是会有一个峰值,后面的值就与实际malloc的值不匹配了,小很多,峰值会记录在resident_size_max变量中。

@aozhimin
Copy link
Owner

aozhimin commented Dec 4, 2017

@simpleease 能贴出你的测试代码么?问题是 malloc 到一定程度后 resident_size 到一个拐点后就递减了,那有与 Debug Gauge 和 Instruments 的值对比么?

@simpleease
Copy link

simpleease commented Dec 5, 2017

@aozhimin 测试的demo可以查看这个:https://github.com/simpleease/MemoryTest
我分别测试了两组: iPhone 5C(10.1.1) 和 iPhone 7(11.1.2)。在iOS11上面比较明显。
Debug Gauge打出来的值与实际分配的是相差不大的。

@aozhimin aozhimin reopened this Dec 5, 2017
@aozhimin
Copy link
Owner

aozhimin commented Dec 5, 2017

@simpleease 多谢提供 Demo,我这边测试了下,通过 resident_size 确实有您上面描述的这个问题,可以被重现,晚上我看下具体原因是什么。

@simpleease
Copy link

@aozhimin 嗯,尝试过分析原因,由于iOS没有换页系统,猜测是不是苹果的一些优化策略,对一些只是初始化后面不再使用的相似内存会unload调,只是用了某个索引表来记录。

@aozhimin
Copy link
Owner

aozhimin commented Dec 6, 2017

@simpleease 下图是 Allocations 和 VM Tracker 的走势
image
Allocations 和 Debug Gauge 一样都是一直递增的,但是 VM Tracker 中的 Resident Size 会到一个峰值然后还会下降一点,和通过 mach API 拿到的趋势基本一致了,API 本身应该没问题。但是为什么会出现这个现象还要进一步看看,你说的只能算是一种猜测,还需要去证明。

@aozhimin
Copy link
Owner

aozhimin commented Dec 8, 2017

@simpleease 今天又去验证了一下,测试机器 iPhone 7 ,OS 11.1.2(15B202),使用 InstrumentsActivity Monitor 模板去跑测试项目,在 Live Processes 一栏中有所有的存活的进程,其中有一列 Real Mem 就是对应进程使用的物理内存,通过 API 拿到的数据与这列的进行对比可以发现两者是一致的,Debug Gauge 应该是和 Allocations 差不多的实现,显示的并不是应用使用的物理内存。

Activity Monitor 的截图

image

API 对应的输出

2017-12-08 08:15:16.697415+0200 MemoryTest[1876:801381] getResidentMemory:684 MB
2017-12-08 08:15:16.707631+0200 MemoryTest[1876:801381] getResidentMemory:685 MB
2017-12-08 08:15:16.717447+0200 MemoryTest[1876:801381] getResidentMemory:686 MB
2017-12-08 08:15:16.727429+0200 MemoryTest[1876:801381] getResidentMemory:687 MB
2017-12-08 08:15:16.737571+0200 MemoryTest[1876:801381] getResidentMemory:688 MB
2017-12-08 08:15:16.747467+0200 MemoryTest[1876:801381] getResidentMemory:689 MB
2017-12-08 08:15:16.757514+0200 MemoryTest[1876:801381] getResidentMemory:691 MB
2017-12-08 08:15:16.767500+0200 MemoryTest[1876:801381] getResidentMemory:692 MB
2017-12-08 08:15:16.777527+0200 MemoryTest[1876:801381] getResidentMemory:693 MB
2017-12-08 08:15:16.787278+0200 MemoryTest[1876:801381] getResidentMemory:694 MB
2017-12-08 08:15:16.797505+0200 MemoryTest[1876:801381] getResidentMemory:695 MB
2017-12-08 08:15:16.807467+0200 MemoryTest[1876:801381] getResidentMemory:696 MB
2017-12-08 08:15:16.817518+0200 MemoryTest[1876:801381] getResidentMemory:697 MB
2017-12-08 08:15:16.827652+0200 MemoryTest[1876:801381] getResidentMemory:698 MB
2017-12-08 08:15:16.837327+0200 MemoryTest[1876:801381] getResidentMemory:682 MB
2017-12-08 08:15:16.847420+0200 MemoryTest[1876:801381] getResidentMemory:683 MB
2017-12-08 08:15:16.857550+0200 MemoryTest[1876:801381] getResidentMemory:684 MB
2017-12-08 08:15:16.867580+0200 MemoryTest[1876:801381] getResidentMemory:685 MB
2017-12-08 08:15:16.877496+0200 MemoryTest[1876:801381] getResidentMemory:686 MB
2017-12-08 08:15:16.887501+0200 MemoryTest[1876:801381] getResidentMemory:687 MB
2017-12-08 08:15:16.897399+0200 MemoryTest[1876:801381] getResidentMemory:688 MB
2017-12-08 08:15:16.907451+0200 MemoryTest[1876:801381] getResidentMemory:689 MB
2017-12-08 08:15:16.917416+0200 MemoryTest[1876:801381] getResidentMemory:690 MB
2017-12-08 08:15:16.927444+0200 MemoryTest[1876:801381] getResidentMemory:691 MB
2017-12-08 08:15:16.937500+0200 MemoryTest[1876:801381] getResidentMemory:692 MB
2017-12-08 08:15:16.947544+0200 MemoryTest[1876:801381] getResidentMemory:693 MB
2017-12-08 08:15:16.959844+0200 MemoryTest[1876:801381] getResidentMemory:694 MB
2017-12-08 08:15:16.967095+0200 MemoryTest[1876:801381] getResidentMemory:695 MB
2017-12-08 08:15:16.977509+0200 MemoryTest[1876:801381] getResidentMemory:697 MB
2017-12-08 08:15:16.998876+0200 MemoryTest[1876:801381] getResidentMemory:682 MB
<End of Run>

关于为什么到峰值会有一点下降,通过 VM Tracker 的数据的比对:

100%	*All*	97	< multiple >	511.81 MiB	450.48 MiB	366.41 MiB	1.55 GiB	32%	
89%	*Dirty*	55	< multiple >	454.28 MiB	450.48 MiB	366.41 MiB	1.34 GiB	33%	
86%	MALLOC_LARGE	12		439.52 MiB	439.52 MiB	351.91 MiB	791.47 MiB	56%	
6%	__LINKEDIT	4	< multiple >	33.66 MiB	0 Bytes	0 Bytes	86.79 MiB	39%	
4%	__TEXT	10	< multiple >	19.25 MiB	0 Bytes	0 Bytes	27.07 MiB	71%	
1%	Performance tool data	7		5.94 MiB	5.94 MiB	14.50 MiB	32.77 MiB	18%	
1%	mapped file	11	< multiple >	4.62 MiB	0 Bytes	0 Bytes	105.19 MiB	4%	
1%	__DATA_CONST	3	< multiple >	3.45 MiB	96.00 KiB	0 Bytes	3.47 MiB	100%	
0%	MALLOC_NANO	1		2.41 MiB	2.41 MiB	0 Bytes	512.00 MiB	0%	
0%	MALLOC_SMALL	1		1.25 MiB	1.25 MiB	0 Bytes	24.00 MiB	5%	
0%	__DATA	5	< multiple >	832.00 KiB	384.00 KiB	0 Bytes	935.04 KiB	89%	
0%	MALLOC_TINY	2		416.00 KiB	416.00 KiB	0 Bytes	4.00 MiB	10%	
0%	MALLOC metadata	15		256.00 KiB	256.00 KiB	0 Bytes	256.00 KiB	100%	
0%	CoreAnimation	3		80.00 KiB	80.00 KiB	0 Bytes	96.00 KiB	83%	
0%	shared memory	3		64.00 KiB	64.00 KiB	0 Bytes	64.00 KiB	100%	
0%	CG raster data	1		32.00 KiB	32.00 KiB	0 Bytes	32.00 KiB	100%	
0%	Activity Tracing	1		32.00 KiB	32.00 KiB	0 Bytes	256.00 KiB	12%	
0%	Foundation	1		16.00 KiB	16.00 KiB	0 Bytes	16.00 KiB	100%	
0%	CG image	1		16.00 KiB	16.00 KiB	0 Bytes	16.00 KiB	100%	
0%	Kernel Alloc Once	1		16.00 KiB	16.00 KiB	0 Bytes	32.00 KiB	50%	
0%	__FONT_DATA	0	CoreText	0 Bytes	0 Bytes	0 Bytes	0 Bytes	NaN	
0%	Stack Guard	5		0 Bytes	0 Bytes	0 Bytes	80.00 KiB	0%	
0%	__UNICODE	0	CoreFoundation	0 Bytes	0 Bytes	0 Bytes	0 Bytes	NaN	
0%	__DATA_DIRTY	0	libc++.1.dylib	0 Bytes	0 Bytes	0 Bytes	0 Bytes	NaN	
0%	MALLOC guard page	10		0 Bytes	0 Bytes	0 Bytes	192.00 KiB	0%	
0%	Stack	0	thread c52a8	0 Bytes	0 Bytes	0 Bytes	0 Bytes	NaN	
100%	*All*	98	< multiple >	452.62 MiB	391.30 MiB	822.98 MiB	1.94 GiB	23%	
87%	*Dirty*	54	< multiple >	395.09 MiB	391.30 MiB	822.97 MiB	1.72 GiB	22%	
85%	MALLOC_LARGE	12		383.41 MiB	383.41 MiB	801.02 MiB	1.16 GiB	32%	
7%	__LINKEDIT	4	< multiple >	33.66 MiB	0 Bytes	0 Bytes	86.79 MiB	39%	
4%	__TEXT	10	< multiple >	19.25 MiB	0 Bytes	0 Bytes	27.07 MiB	71%	
1%	mapped file	11	< multiple >	4.62 MiB	0 Bytes	0 Bytes	105.19 MiB	4%	
1%	__DATA_CONST	3	< multiple >	3.45 MiB	96.00 KiB	0 Bytes	3.47 MiB	100%	
1%	Performance tool data	7		3.36 MiB	3.36 MiB	21.48 MiB	32.77 MiB	10%	
0%	MALLOC_NANO	1		2.39 MiB	2.39 MiB	16.00 KiB	512.00 MiB	0%	
0%	MALLOC_SMALL	1		880.00 KiB	880.00 KiB	400.00 KiB	24.00 MiB	4%	
0%	__DATA	5	< multiple >	832.00 KiB	384.00 KiB	0 Bytes	935.04 KiB	89%	
0%	MALLOC_TINY	2		336.00 KiB	336.00 KiB	80.00 KiB	4.00 MiB	8%	
0%	MALLOC metadata	15		256.00 KiB	256.00 KiB	0 Bytes	256.00 KiB	100%	
0%	CoreAnimation	3		64.00 KiB	64.00 KiB	0 Bytes	64.00 KiB	100%	
0%	shared memory	3		64.00 KiB	64.00 KiB	0 Bytes	64.00 KiB	100%	
0%	CG raster data	1		32.00 KiB	32.00 KiB	0 Bytes	32.00 KiB	100%	
0%	Activity Tracing	1		32.00 KiB	32.00 KiB	0 Bytes	256.00 KiB	12%	
0%	Foundation	1		16.00 KiB	16.00 KiB	0 Bytes	16.00 KiB	100%	
0%	CG image	1		16.00 KiB	16.00 KiB	0 Bytes	16.00 KiB	100%	
0%	Kernel Alloc Once	1		16.00 KiB	16.00 KiB	0 Bytes	32.00 KiB	50%	
0%	__FONT_DATA	0	CoreText	0 Bytes	0 Bytes	0 Bytes	0 Bytes	NaN	
0%	dylib	1		0 Bytes	0 Bytes	0 Bytes	16.00 KiB	0%	
0%	Stack Guard	5		0 Bytes	0 Bytes	0 Bytes	80.00 KiB	0%	
0%	__UNICODE	0	CoreFoundation	0 Bytes	0 Bytes	0 Bytes	0 Bytes	NaN	
0%	__DATA_DIRTY	0	libc++.1.dylib	0 Bytes	0 Bytes	0 Bytes	0 Bytes	NaN	
0%	MALLOC guard page	10		0 Bytes	0 Bytes	0 Bytes	192.00 KiB	0%	
0%	Stack	0	thread c52a8	0 Bytes	0 Bytes	0 Bytes	0 Bytes	NaN	

MALLOC_LARGE是代码中每次 Malloc 1MB 的内存,可以看到随时间推移,这部分有一些下降。

@simpleease
Copy link

@aozhimin VM Tracker中的Swapped Size后面不为0,怎么解释呢?

@aozhimin
Copy link
Owner

aozhimin commented Dec 8, 2017

@simpleease 这个我也注意到了,但是比较老的文档都表示 iOS 没有交换空间的实现,也没有公开的文档显示 iOS 新增了这一特性,但是 VM Tracker 的 Swapped Size 确实会在内存快速增长到一定值后,它也会从原来的初始值 0 随之增长,而且 Instrument-VMTracker 中也没提及 Swapped Size 列,所以有可能是新加的特性,但是确实找不到证据来证明。

@27629678
Copy link

@sohotz
Copy link

sohotz commented Mar 19, 2018

用phys_footprint就和xcode显示的内存一致了

@kojiyijian
Copy link

@feel2d phys_footprint和code的显示接近, 变化趋势一致.
但数值并不一致,不同阶段统计值和xocde显示值的误差也不一样。

@sohotz
Copy link

sohotz commented Mar 19, 2018

@kojiyijian ,我之前debug看的时候,xcode显示和自己log的基本很一致,太详细的再没有研究。你说的“不同阶段统计值和xocde显示值的误差也不一样“, 不同阶段是指?另外,phys_footprint 是几部分内存之和,Jetsam 和webcore好像都用了, http://newosxbook.com/articles/MemoryPressure.html

@yinjining
Copy link

楼主还是没解释清楚为什么代码统计的内存会变大,能在详细解释下吗?不胜感激

@aozhimin
Copy link
Owner

@yinjining@onerobot 所说,resident_size(驻留内存)确实无法反映应用的真实物理内存,而且 Xcode 的 Debug Gauge 使用的应该是 phys_footprint,这个从 WebKit 和 XNU 的源码都能够得到佐证。WebKit 代码

size_t memoryFootprint()
{
    task_vm_info_data_t vmInfo;
    mach_msg_type_number_t count = TASK_VM_INFO_COUNT;
    kern_return_t result = task_info(mach_task_self(), TASK_VM_INFO, (task_info_t) &vmInfo, &count);
    if (result != KERN_SUCCESS)
        return 0;
    return static_cast<size_t>(vmInfo.phys_footprint);
}

XNU 代码 中 Jetsam 中判断应用内存是否过大使用的也是 phys_footprint,2018 WWDC Session iOS Memory Deep Dive 对这块也有介绍,有兴趣可以去看下。

@Humble7
Copy link

Humble7 commented Dec 4, 2018

或许是resident_size不包括cleanMemory

@isee15
Copy link

isee15 commented Feb 18, 2019

task_vm_info_data_t info;
mach_msg_type_number_t size = TASK_VM_INFO_COUNT;
kern_return_t kerr = task_info(mach_task_self(),
TASK_VM_INFO,
(task_info_t)&info,
&size);
if( kerr == KERN_SUCCESS ) {
mach_vm_size_t totalSize = info.internal + info.compressed;
NSLog(@"Memory in use (in bytes): %u", totalSize);
return totalSize;
} else {
NSLog(@"Error with task_info(): %s", mach_error_string(kerr));
}

@JethroYe
Copy link

JethroYe commented Feb 1, 2021

可以查看下这个form https://developer.apple.com/forums/thread/105088 , Apple 的工程师提供了建议,建议使用 phys_footprint
Lark20210201-160826

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests