Android 内存优化浅析

一:内存占用几大要点

1,Object Cache:Image cache,single instance obj(重量级别,例如数据库连接obj,bitmap ref),Thread过多,

2,View Ref过多:view 本身结构嵌套过多,过于复杂,background子元素image过多,使得单个view对象占有内存较多,如果View Container含有这实例对象过多,则会导致oom

3,组建Activity,service过多,也是会使app内存占用过高

4,leak Memmory:叠加效应导致内存异常占用

(简单叙述一下泄漏主因:全局成员变量引用(包括间接引用)components(Activity,Service,Broadcast,View及内部非static类),导致其无法回收资源),

5,有必要在合适的时候进行Trim cache

开发中注意以下几个方面:

(1)字符串拼接优化,减少字符串使用加号拼接,改为使用StringBuilder,初始化时设置capacity。

(2) 读文件优化 读文件使用ByteArrayPool,初始设置capacity,减少expand

(3) 资源重用,建立缓存池,对频繁申请、释放的对象类型重用

(4) 减少不必要或不合理的对象,例如在ondraw、getview中应减少对象申请,尽量重用。例如循环中不断申请局部变量等

(勿在循环调用的地方去产生对象,比如很多人不会注意的在getview里new onclicklistener(),这样的方式拖动的次数越多那么就会产生越多的对象。

当然还有在onDraw的地方newPaint等操作,这些都是需要避免的)

(5) 选用合理的数据格式 使用SparseArray,SparseBooleanArray, and LongSparseArray来代替Hashmap

 Enum的内存消耗通常是static constants的2倍。应尽量避免在Android上使用enum,避免使用array 存储objects,(最好确定元素个数)

(6)Layout过于复杂,View频繁的触发measure、layout对View layout param进行频繁调整,例如动画效果,getview不同View gone visible param的调整

(7)同一时间动画执行的次数过多,View过度绘制,导致某些像素在同一帧时间内被绘制多次,从而使CPU或GPU负载过重;

(8)使用多进程,对于webview等,由于存在内存系统泄露或者占用内存过多的问题,我们可以采用单独的进程。微信放在单独的tools进程中

(9)动画问题:动画的每一帧渲染都是在UI线程的,如果有动画的时候进行耗时操作,很可能导致动画不流畅,掉帧问题,
    耗时操作包括:
    Layout:当动画正在播放的时候,要避免改变View(延迟改变);同时选择动画也需要避免会触发layout的动画,例如translation,scale等会导致延迟的layout操做。          Inflation:动画过程中避免inflate新的view,比如启动新的activity,或ListView(ScorllView)滑动到不同type的区域。


二:内存使用监控

1,GC日志监控

Dalvik Log Messages:

D/dalvikvm: <GC_Reason> <Amount_freed>, <Heap_stats>, <External_memory_stats>, <Pause_time>
example
D/dalvikvm( 9050): GC_CONCURRENT freed 2049K, 65% free 3571K/9991K, external 4703K/5261K, paused 2ms+2ms

通过GC log看到每次回收多少,可以内存剩余多少,这里重点关注GC_Reason,

GC_CONCURRENT
内存使用到达目标使用率(系统会有一个目标使用率例如,45%)时,开时回收内存(A concurrent GC that frees up memory as your heap begins to fill up).
GC_FOR_MALLOC
剩余内存不足,小于对象申请使用内存大小,此时暂停程序所有线程,开始清理回收内存
(A GC caused because your app attempted to allocate memory when your heap was already full, so the system had to stop your app and reclaim memory.)
GC_HPROF_DUMP_HEAP
dump hprof时,引起内存回收(A GC that occurs when you request to create an HPROF file to analyze your heap.)


ART Log Messages
I/art : Explicit concurrent mark sweep GC freed 104710(7MB) AllocSpace objects, 21(416KB) LOS objects, 33% free, 25MB/38MB, paused 1.230ms total 67.216ms

Concurrent
(并发回收,垃圾回收不影响程序运行)
A concurrent GC which does not suspend app threads. This GC runs in a background thread and does not prevent allocations.
Alloc
(程序申请内存,内存不够时触发)
The GC was initiated because your app attempted to allocate memory when your heap was already full. In this case,
the garbage collection occurred in the allocating thread.
Explicit
(调用System.gc()触发,没啥实际用途)
The garbage collection was explicitly requested by an app, for instance, by calling gc() or gc(). As with Dalvik,
in ART it is recommended that you trust the GC and avoid requesting explicit GCs if possible.
NativeAlloc
(jni层进行内存申请时触发)
The collection was caused by native memory pressure from native allocations such as Bitmaps or RenderScript allocation objects.


2,Runtime监控

Runtime.getRuntime().maxMemory();  
long leaveMemmory= Runtime.getRuntime().totalMemory()-Runtime.getRuntime().freeMemory();
//我们可以定期(前台每隔3分钟)去得到这个值,当我们这个值达到危险值时(例如85%)手动清理资源

3,Compoment onMemmoryTrim()回调监控,涉及到Android内存回收策略

系统内存回收策略:我们知道当应用不再使用退出后,系统不会立即终止掉该app进程而是将它缓存起来(方便下次快速启动),当系统内存不够使用时,

system会按照process优先级别进行处理,这个process列表使用LRU(last recently used)规则处理

process分类:引用文档一段描述

(1)foreground process     what the user is currently doing
(2)visible process    holding an Activity that is visible to the user on-screen but not in the foreground (its onPause() method has been called).

the foreground Activity is displayed as a dialog

(3)service process    holding a Service that has been started
(4)background process     holding an Activity that is not currently visible to the user (its onStop() method has been called).
(5)empty process     that doesn't hold any active application components.(application quit)

@Override
	public void onTrimMemory(int level) {
		//Called when the operating system has determined that for a process to trim unneeded memory from its process
		//内存不够新程序内存申请时,开始回收,callback this method
		switch(level){
		case TRIM_MEMORY_COMPLETE:
			//process is nearing the end of the background LRU list(LRU后台进程列表中最后进程,内存不足时会被杀死)
			break;
		case TRIM_MEMORY_MODERATE:
			//process is around the middle of the background LRU list(LRU后台进程列表中中间进程,内存不足时可能会被清理)
		case TRIM_MEMORY_BACKGROUND:
			//process has gone on to the LRU list(程序进入后台LRU列表)
			break;
		case TRIM_MEMORY_UI_HIDDEN:
			//process had been showing a user interface, and is no longer doing so (程序对用户不可见,UI不在对用户可见,此时应该released UI resource)
		case TRIM_MEMORY_RUNNING_CRITICAL:
			//the device is running extremely low on memory and is about to not be able to 
			//keep any background processes running(LRU后台Process已全部清理,可用内存依然很低不够使用,此时需要当前程序手动释放资源,缓存,图像,不可见view等)
		case TRIM_MEMORY_RUNNING_LOW:
			break;
		}
		super.onTrimMemory(level);
	}

总结:

1,在平时编写代码时注意已经明确出会产生内存问题代码写法

2,注意模块内存泄漏监测,Allocation Tracker,Leankcanary

3,Compoment内根据其生命周期,进行资源释放控制,例如:Activity onpause,onResume注册,解除注册

4,内存警戒值检测,当达到预警值释放,image,object cache等清理,多个Activity会有很多后台view 并不对用户可见,此时也可以释放掉,

数据存盘,View至空处理,当页面返回时重新加载view

5,设计时候减少后台资源


参考

https://developer.android.com/studio/profile/investigate-ram.html

http://mp.weixin.qq.com/s?__biz=MzAwNDY1ODY2OQ==&mid=400656149&idx=1&sn=122b4f4965fafebf78ec0b4fce2ef62a&scene=23&srcid=0701zlpPcKRDEIZB7BRu3bdj#rd



原文地址:https://www.cnblogs.com/happyxiaoyu02/p/6150737.html