Android应用中OOM问题剖析和解决方案
1.什么是OOM?
03-21 21:05:28.771: E/dalvikvm-heap(13316): Out of memory on a 10485776-byte allocation.
03-21 21:05:28.779: E/AndroidRuntime(13316): java.lang.OutOfMemoryError
这几句的意思是,我们程序申请需要10485776byte太大了,虚拟机无法满足我们,羞愧的shutdown自杀了。这个现象通常出现在用到很多图片或者很大图片的APP开发中。通俗讲就是当我们的APP需要申请一块内存来装图片的时候,系统觉得我们的APP所使用的内存已经够多了。即使它有1G的空余内存,它不同意给我的APP更多的内存里,然后即使系统马上抛出OOM错误,而程序没有捕捉该错误,故弹框崩溃了。
因为android系统的app的每个进程或者每个虚拟机有个最大内存限制,如果申请的内存资源超过这个限制,系统就会抛出OOM错误。跟整个设备的剩余内存没太大关系。比如比较早的android系统的一个虚拟机最多16M内存,当一个app启动后,虚拟机不停的申请内存资源来装载图片,当超过内存上限时就出现OOM。Android系统的APP内存限制怎么确定?
2.1 Android的APP内存组成:
APP内存由
每个手机的内存限制大小是多少?
ActivityManager activityManager = (ActivityManager)context.getSystemService(Context.ACTIVITY_SERVICE)
activityManager.getMemoryClass();
以上方法会返回以M为单位的数字,不同的系统平台或设备上的值都不太一样,比如HTC默认24M, Galaxy36M, emulator-2.3 24M,等等。我的moto xt681是42M。3
上面取到是虚拟机的最大内存资源。
而对于head堆的大小限制,可以查看/system/build.prop文件。
dalvik.vm.heapstartsize = 5m
dalvik.vm.heapgrowthlimit = 48m
dalvik.vm.heapsize = 256m
注: heapsize参数表示单个进程heap可用的最大内存,但如果存在以下参数"dalvik.vm.headgrowthlimit =48m"表示单个进程heap内存被限定在48m,即程序运行过程实际只能使用48m内存。
2.2 为什么android系统设定APP的内存限制?
1 要使开发者内存使用更为合理。 限制每个应用的可用内存上限,可以放置某些应用程序恶意或者无意的使用过多的内存。而导致其它应用无法正常运行。Android是有多进程的,如果一个进程(就是一个应用)耗费过多的内存,其他应用就无法运行了。因为有了限制,使得开发者必须好好利用有限资源,优化资源的使用。
2 屏幕显示内容有限,内存足够即可。 即使有万千图片千万数据需要使用到,但在特定时刻需要展示给用户看的总是有限的,因为屏幕显示就那么大,上面可以放的信息就是很有限的。大部分信息都是处于准备显示状态,所以没必要给予太多heap内存。也就是说出现 OOM现象,绝大部分原因是我们的程序设计上有问题,需要优化 。优化方法很多,比如通过时间换空间,不停的加载要用的的图片,不停的回收不用的图片,把大图片解析成适合手机屏幕大小的图片等。
3 多APP多个虚拟机davlik的限制需要。 android上的app使用独立虚拟机,每开一个应用就会打开至少一个独立的虚拟机。这样可以避免虚拟机崩溃导致整个系统崩溃,同时代价就是需要浪费更多的内存。这样设计保证了android的稳定性。
2.3 不是GC自动回收资源么,为什么还会OOM?
Android不是用GC会自动回收资源么,为什么app的那些不用的资源不回收呢?
Android的gc会按照特定的算法回收程序不用的内存资源,避免app的内存申请约积越多,但是gc一般回收的资源是那些无主的对象内存或者软引用的资源,或者更软引用的引用资源。比如:
Bitmap bt = BitmapFactory.decodeResource( this .getResources(), R.drawable.splash); //此时的图片资源是强引用,是有主的资源。
bt = null ; //此时这个图片资源就是无主的了。gc心情号的时候就会去回收它。
SoftReference<Bitmap> softRef = new SoftReference<Bitmap>(bt);
bt = null ;
其他代码...
当程序申请很多内存资源时,gc有可能会释放softref引用的这个图片内存。bt=softRef.get(),此时可能得到的是null,需要重新加载图片。
当然这也说明了用软引用图片资源的好处,就是gc会自动根据需要释放资源,一定程度上避免OOM。
TIPS:编程要养成习惯,不用的对象设置为null。其实更好的是,不用的图片直接recycle。因为通过设置null让gc来回收,有时候还是会来不及。
2.4 怎么查看APP内存分配情况?
1 通过DDMS中的heap选项卡监视内存情况:
Heap视图中部有一个叫做data object, 即数据对象,也就是我们的程序中大量存在的类类型的对象。
在data object一行中有一列是"Total Size", 其值就是当前进程中所有Java数据对象的内存总量。如果代码中存在没有释放对象引用的情况,则data object的"Total Size"值在每次gc后不会有美线的回落。随着操作次数的增加"Total Size"的值会越来越大。直到到达一个上限 后导致进程被kill掉。
2 在App里面我们可以通过totalMemory与freeMemory:
Runtime.getRuntime().freeMemory()
RUntime.getRuntime().totalMemory()
3 adb shell dumpsys meminfo com.android.demo
3. 常见避免OOM的几个注意点:
3.1 适当调整图像大小 。因为手机屏幕尺寸有限,分配给图像的显示区域有限,尤其对于超大图片,加载自网络或者sd卡,图片文件提及达到几M或者十几个M的:
加载到内存前,先算出该bitmap的大小,然后通过适当调节采样率使得加载的图片刚好,或稍大捷克在手机屏幕上显示就满意了:
BimtapFactory.Option opts = new BitampFactory.Option();
opts.inJustDecodeBounds = true ;
opts.inSampleSize=computeSample(opts, minSideLength, maxNumOfPixels); // Android 提供了一种动态计算的方法 computeSampleSize
opts.inJustDecodeBounds = false ;
try {
return BitmapFactory.decodeFile(imageFile, opts);
} catch (OutOfMemoryError err){
}
3.2 图像缓存 。在listview或Gallery等控件中一次性加载大量图片时,只加载屏幕显示的资源,尚未显示的不加载,移出屏幕的资源及时释放,采用强引用+软引用2级缓存,提高加载性能。缓存图像到内存,采用软引用缓存到内存,而不是在每次使用的时候都从新加载到内存。
3.3 采用低内存占用量的编码方式 。比如Bitmap.Config.ARGB_4444比Bitmap.Config.ARGB_8888更省内存。
3.4 及时回收图像 。如果引用了大量的Bitmap对象,而应用又不需要同时显示所有图片。可以将暂时不用到的Bitmap对象及时回收掉。对于一些明确直到图片使用情况的场景可以主动recycle回收
App的启动splash画面上的图片资源,使用完就recycle。对于帧动画,可以加载一张,画一张,释放一张。
3.5 不要在循环中创建过多的本地变量 。慎用static,用static来修饰成员变量时,该变量就属于该类,而不是该类实例,它的生命周期是很长的。如果用它来引用一些内存占用太多的实例,这时候就要谨慎对待了。
3.6 自定义堆内存分配大小 。优化Dalvik虚拟机的堆内存分配。
public class ClassName{
private static Context mContext;
// 省略
}
4. App使用图片时避免OOM的几种方式:
4.1 直接null或recycle
对于app里使用的大量图片,采用方式:使用时加载,不显示时直接置null或recycle。
这样处理是个好习惯,记本可以杜绝OOM,但是缺憾是代码多了,可能会忘记某些资源recycle。
而有些情况下会出现特定图片反复加载,释放,再加载等,低效率的事情。
4.2 简单通过SoftReference引用方式管理图片资源
建个SoftReference的hashmap
使用图片时先查询这个hashmap是否有softreference, softreference里的图片是否为空,
如果为空就加载图片到softreference并加入hashmap。
无需再代码里显式的处理图片的回收与释放,gc会自动处理资源的释放。
这种方式处理起来简单实用,能一定程度上避免前一种方法反复加载释放的低效率。但还不够优化。
4.3 强引用+软引用二级缓存
Android示范程序ImageDownloader.java, 使用了一个二级缓存机制。就是有一个数据结构直接持有解码成功的Bitmap对象引用,同时使用一个二级缓存数据结构保持淘汰的Bitmap的softreference对象,由于softreference对象的特殊性,系统会再需要内存的时候首先将softreference持有的对象释放掉,也就是说当vm发现可用的内存较少需要出发gc的时候,二级缓存中的bitmap对象将被回收,而持有一级缓存的bitmap对象用于显示。
其实这个解决方案最为关键的一点是使用了一个比较合适的数据结构,那就是LinkedHashMap类型来进行一级缓存Bitmap的容器。由于LinkeHashMap的特殊性,我们可以控制其内存存储对象的个数并且将不在使用的对象从容器中移除,放到softreference二级缓存里,我们可以在一级缓存中一致保存最近被访问到的bitmap对象,而已经被访问过的图片在LinkedHashMap的容量超过我们预设值时将会把容器中存在的时间最长的对象移除,这个时候我么可以将被移除的LinkedHashMap中的放到二级缓存容器,而二级缓存中的对象管理就交给系统来做了,当系统需要gc时就会首先回收二级缓存容器的Bitmap对象了。
在获取图片对象时候先从一级缓存容器中查找,如果有对应对象并可用直接返回,如果没有的话从二级缓存中查找对应的SoftReference, 判断SoftReference对象持有的Bitmap是否可用,可用直接返回,否则返回空。如果二级缓存都找不到图片,那就直接加载图片资源。
4, LruCache + sd的缓存方式
5. 两种容易OOM的场景建议:
5.1 网络下载大量图片
比如微博客户端: 多线程异步网络,小兔直接用LRUCache+SoftRef+Sd,大图按需下载:
5.2 对于需要加载非常多条目信息的listview,gridview等的情况
在adapter的getView函数里有个convertView参数,告知你是否有可重用的view对象。 如果不使用convertView的话,每次调用getView时每次都会重新创建view,这样之前的view可能还没销毁,加之不断的新建view势必会造成内存剧增,从而导致OOM。另外在重用convertView时,里面原有的图片等资源就会变成无主的了。
这里Google官方推荐使用:"convertview+静态类viewholder"
官方给出解释是:
a 重用缓存convertView传递给getView()方法来避免填充不必要的视图。
b 使用ViewHolder模式来避免没有必要的调用findViewById;因为太多的findViewById也会影响性能。
附ViewHolder类的作用:ViewHolder模式通过在getView方法返回的视图的标签(tag)中存储一个数据结构。这个数据结构包含了指向我们要绑定数据的视图的引用,从而避免每次调用getView()的时候调用findViewById();
6 申请超过内存限制的内存分配方式:
6.1 从Native C分配内存。使用NDK(本地开发工具包)和JNI, 它可能从C级(如malloc/free或新建/删除)分配内存,这样的分配是不计入24MB的限制。这是真的,从本机代码分配内存是为了java方便,但它可以被用来存储在ram的数据(即使图片数据)的一些打击呢。
6.2 使用OpenGL的纹理。纹理内存不计入限制,要查看你的应用程序确实分配了多少内存可以使用android.os.Debug.getNativeHeapAllocatedSize(), 可以使用上面介绍的两种技术的Nexus之一,我可以轻松地为一个单一的前台进程分配300MB-10倍以上的默认24MB 的限制,从上面看来使用native代码分配内存是不在24MB的限制内的(开放的GL的质地也是使用native代码分配内存)。
但是,这两个方法的风险就是,本地堆分配内存超过系统可用内存限制的话,通常都是直接崩溃。
moto S30 Pro Pantone限量版标配有何不同?Moto新68W氮化镓快充拆解
前言
去年年底MOTO摩托罗拉发布了多款新机,其中Moto S30 Pro Pantone限量版搭载高通骁龙888+平台,内置4400mAh电池以及支持68W有线快充,与市面同定位机型相比表现还不错。
最近充电头网拿到了这款手机标配68W氮化镓充电器美规版本,充电器由江苏辰阳电子有限公司代工生产,型号为MC-681N。据了解,这款充电器依旧采用杰华特电源方案以及英诺赛科氮化镓器件,不过型号有所变化。
下面充电头网就对摩托罗拉68W氮化镓充电器美版进行详细拆解,一起看看充电器内部具体情况。
摩托罗拉68W氮化镓充电器外观
充电器采用经典直板造型,外壳磨砂抗指纹,正背面设计则和之前拆解的摩托罗拉系列快充一样。
机身正面中心设计有摩托罗拉品牌logo。
背面是亮面的68W字样,也就是充电器的最大输出功率。
充电器配备固定美规插脚。
输入端外壳印有产品参数
型号:MC-681N
输入:100-240V~50/60Hz 2A
输出:5V3A、9V3A、15V3A、20V3.4A、11V6.2A
制造商:江苏辰阳电子有限公司
产品已经通过了PSE、UL、NOM、NYCE认证,以及VI级能效认证。
顶面配有单USB-C接口,居中布局。
测得充电器机身长度为58.06mm。
宽度为41.96mm。
厚度为27.91mm。
和苹果61W充电器对比,体积优势明显。
充电器拿在手上的大小直观感受。
另外测得充电器净重约为73g。
使用ChargerLAB POWER-Z KM003C测得USB-C口支持QC3.0/5、PD3.0、PPS、DCP、Apple 2.4A充电协议。
并且具备5V3A、9V3A、15V3A、20V3.4A四组固定电压档位,以及3.3-11V5A和3.3-20V3.4A两组PPS电压档位。
摩托罗拉68W氮化镓充电器拆解
看完了摩托罗拉68W氮化镓充电器的外观和尺寸,下面就对这款充电器进行拆解,看看内部设计和用料。
拆开充电器外壳取出PCBA模块。
测得PCBA模块长度为53.89mm。
宽度为38.06mm。
厚度为22.22mm。
变压器前后打胶加固,变压器和USB-C母座间还设有隔离板,电解电容顶部粘贴泡棉。
PCB板背面覆盖塑料板和散热板。
将塑料板拆下,其与正面隔离板一体式设计,电路板上核心器件打胶帮助导热。
板子正面设有保险丝、输入输出电容、变压器等器件。
PCB板背面一览,光耦横跨初次级,还可以看到背面有开关电源主控芯片、氮化镓开关管、同步整流控制器以及输出VBUS开关管。
输入端一览,焊接保险丝、NTC热敏电阻、安规X电容、共模电感、工字电感。
保险丝外套热缩管保护。
安规X电容来自STE松田电子,容量0.1μF。
NTC热敏电阻和共模电感同样外套热缩管。
输入端采用四颗二极管代替整流桥进行整流。
板子一侧设有三颗Acon中元高压滤波电解电容。
其中两颗电容规格为400V 22μF。
一颗规格为400V 27μF。
板子另一侧设有主控芯片供电电容以及变压器。
主控芯片供电电容也是来自中元,规格为100V 10μF。
主控芯片来自杰华特,型号JW1515HB,是一颗高度集成的准谐振反激控制器,支持700V高压启动,集成X电容放电功能,支持8~83V宽范围供电,无需外加稳压元件。内置高可靠GaN直驱电路,6V驱动电压可直接驱动增强型GaN器件,有效简化驱动电路设计,可靠性更高。
JW1515HB采用SSOP-10封装,支持最高170KHz开关频率,支持多模式运行,在不同的输入和负载条件下获得最高的平均效率。芯片具有极低的待机功耗,具备逐周期过流保护及完善的保护功能,适用于PD及QC充电器应用。
杰华特JW1515HB资料信息。
初级开关管来自英诺赛科,为定制料号,型号INN650DAL02A,是一颗耐压650V的增强型氮化镓高压单管。器件采用DFN5*6封装,支持超高开关频率,无反向恢复电荷,具有开尔文源极,可应用于DCM/BCM PFC、AHB、LLC、QR、ACF等电路,满足快充、笔电适配器、PC/ATX/TV电源设计需求。
英诺赛科 INN650DAL02A 资料信息。
充电头网拆解了解到,英诺赛科氮化镓芯片目前已被三星,OPPO,VIVO,联想,雅迪,LG,华硕,安克,努比亚,倍思,绿联,闪极等多家知名品牌和厂商所采用,出货量突破1.2亿颗。
变压器特写。
输出端一览,设有两颗蓝色Y电容、两颗固态电容。
EL 1019光耦,用于输出电压反馈。
两颗蓝色Y电容来自松田电子。
同步整流控制器来自杰华特,丝印JWEWJ,型号JW7726B,支持CCM、DCM、QR和ACF模式,在振铃期间能有效防止同步整流MOSFET的误开通;具有快速关断能力以便能兼容CCM;在启动过程中(VCC建立之前)能有效防止门极gate被耦合至开启电压,支持高侧和低侧同步整流应用,使用灵活。
杰华特JW7726B具有低静态电流,输出电压检测功能用于自动切换供电。当输出电压高于4.6V时,由输出电压供电,以降低稳压器上的损耗。当输出电压低于4.5V时,由开关引脚供电。JW7726B采用SOT23-6封装,外围器件精简。
充电头网拆解了解到,杰华特JW7726B还被闪极Retro 35W氮化镓充电器、公牛67W 3C1A氮化镓快充插座、联想ThinkBook 65W饼干氮化镓充电器、睿元实业33W 1A1C氮化镓快充、REMAX 30W氮化镓充电器等产品采用。
同步整流管来自威兆,型号VSP004N10MS-G,是一颗耐压100V的NMOS,导阻为3.8mΩ,采用PDFN5060X封装。
威兆半导体 VSP004N10MS-G 详细资料。
充电头网拆解了解到,威兆MOS管广泛应用在充电器、无线充、车充、充电宝、储能电源等各个方面,并被华为、小米、紫米、三星、OPPO、VIVO、魅族、努比亚、倍思、公牛、网易智造等知名品牌的数十款产品采用,产品质量获得客户一致认可。
两颗输出滤波固态电容来自PolyCap柏瑞凯,规格470μF 25V,使用两颗并联用于输出滤波,SV系列是柏瑞凯为了满足客户插件产品过通孔回流焊的生产工艺需求,特推出的插件固态铝电容器系列,产品能够承受回流焊的高温、系列规格型号丰富。
充电头网拆解了解到,柏瑞凯的产品广泛应用于充电器、车充、无线充、移动电源、储能电源等领域,并被OPPO、vivo、小米、荣耀、三星、华为、联想、紫米、努比亚、公牛、ANKER等知名品牌的多款产品采用,产品获得市场的高度认可。
协议芯片采用英集芯IP2726,这是一颗集成多种协议、用于USB输出端口的快充IC,支持Type-C DFP、PD2.0/3.0、PPS、QC4+、QC2.0/3.0、FCP、AFC、SCP、MTK PE+、Apple2.4A、BC1.2以及三星2A等协议。具有高集成度和丰富的功能,在应用时仅需极少的外围器件,有效减小整体方案的尺寸,降低BOM成本。
充电头网了解到,英集芯IP2726已经通过了高通QC4+认证,拿到了中国大陆地区第一张QC4+认证证书。
充电头网了解到,英集芯IP2726此前已被AUKEY 65W 1A1C氮化镓快充充电器、RAVPOWER 90W双USB-C口PD快充充电器、斯泰克100W双USB-C口氮化镓充电器、麦多多30W 1A1C USB PD快充充电器、坤兴18W USB PD3.0 PPS快充充电器等产品采用。此外,英集芯的其它系列快充芯片也获得小米、华为、三星等客户的高度认可。
USB-C接口的VBUS开关管来自威兆半导体,型号VS3618BE,是一颗耐压30V的NMOS,导阻为5.2mΩ,采用PDFN3333封装。
威兆半导体 VS3618BE 详细资料。
充电头网通过拆解了解到,采用威兆 VS3618BE的产品还有努比亚20W PD快充充电器、小米11原装55W氮化镓充电器、航嘉55W USB PD快充充电器、小米33W PD快充、小米手机原装30W快充等,威兆MOS的产品性能也获得众多知名品牌认可。
USB-C母座特写。
全部拆解一览,来张全家福。
充电头网拆解总结
摩托罗拉68W氮化镓充电器美版相较国行版除插脚做了相应更换外,机身外观设计一样,并且支持QC3.0、PD、PPS快充,支持68W PD以及摩托罗拉私有快充,满足摩托罗拉手机用户快充外,还能拿来充笔电、平板等设备,设计上算是比较友好。
充电头网通过拆解了解到,充电器采用了杰华特JW1515HB+JW7726B开关电源方案,分别搭配英诺赛科INN650DAL02A氮化镓开关管以及威兆整流管,滤波电容分别采用中元和柏瑞凯两大品牌,由英集芯IP2726进行协议识别控制输出。充电器部分器件相较早期款式做了升级迭代,整体用料依旧十分扎实可靠。
相关问答
摩托罗拉 xt681 的手机开机不了。如何恢復出厂设置?-ZOL问答摩托罗拉xt681的手机开机不了。如何恢復出厂设置?举报摩托罗拉xt681的手机开机不了。如何恢復出厂设置?手机MotoMotoX39人讨论8939次围观关注问题写...
等手机换代再用好吗?2011年到现在用过(HTCG10,HTCG17,HTCBUTTERFLYS,HTCONEM7,HTC10,华为火花S8600,华为C8812D,华为荣耀1,三星NOTE3(国行和美...