做软件下载网站怎么赚钱,小程序制作需要什么语言,企业建设网站的好处有哪些,凡科建站如何制作论坛#x1f525; OOM介绍#xff08;out of memory 内存溢出#xff09;#x1f525; Android和java中都会出现由于不良代码引起的内存泄露#xff0c;为了使Android应用程序能够快速高效的运行#xff0c;Android每个应用程序都会有专门Dalvik虚拟机实例来运行#xff0c;… OOM介绍out of memory 内存溢出 Android和java中都会出现由于不良代码引起的内存泄露为了使Android应用程序能够快速高效的运行Android每个应用程序都会有专门Dalvik虚拟机实例来运行也就是每个程序都在属于自己的进程中运行。 某个应用程序内存泄露仅仅只会使自己进程被kill掉不会影响其他进程如果是system_process等系统进程出现问题就会造成系统重启另一方面系统为每一个应用程序分配了不同的内存上限如果超过这个上限被视为内存泄露从而被kill掉。 Dalvik Heap size因不同设备的RAM不同而有所差异应用占用内存接近这个阀值在尝试分配内存就会引起outofmemoryError的错误。 出现OOM的几种情况 1、加载对象过大 2、相应资源过多来不及加载。 解决方案 : 1、内存引用上做一些处理常用的有软引用。 2、内存中加载图片直接在内存中做处理如边界压缩 3、这个GlideFresco 图片框架可能封装好了 4、动态回收内存 5、优化Delivk虚拟机的堆内存分配 6、自定义堆内存大小 共享内存 Android应用程序的进程都是从Zygote的进程fork出来的。Zygote进程在系统启动并载入通用的framework代码和资源后启动。一个新的应用程序启动系统就会从Zygote中fork出来一个新的进程在新的进程中加载并允许应用程序的代码。这使得大多数RAM pages被分配给framework的代码并且RAM资源能够在应用的所有进程之间共享。 大多数static 数据被mmapped到一个进程中这样使得同样的数据在进程之间能够共享而且在需要的时候能paged out.常见static 数据包括Dalvik code ,app resourecs,so 文件等。 大多数情况下Android通过显示的方式分配共享内存区域例如ashmem或gralloc来实现动态RAM区域能够在不同进程之间进行共享的机制。比如Window Surface在APP和Screen Composition之间使用共享的内存 Cursor Buffers在Content Provider与Clients之间共享内存。 分配与回收内存 每个进程的Dalvik heap都反应了使用内存的占用范围,Dalvik Heap Size,他可以根据需要进行增长但是系统有一个上限。 HeapSize跟实际的物理内存大小是不对等的PSSproportional Set Size记录了应用程序自身占用以及和其他进程共享的内容。 Android不会对heap空闲区域进行做碎片整理。系统仅仅在新的内存分配之前判断Heap的尾端剩余空间是否足够不够就会触发gc操作从而腾出更多空闲的内存空间。 gc操作(garbage collection)也就是所谓的垃圾回收Android在适当时候触发gc操作将一些不再使用的对象回收在Android高级系统针对Heap空间有一个Generational Heap Memory的模型最近分配的对象在放在young generation区域当停留一段时间这个对象会被移动到old generation中最后在移动到permanent generation区域中。 系统会根据内存中不同的内存数据类型进行gc操作young generation区域的对象更容易被销毁而且gc操作的速度比old generation的速度要快时间更短。 每个generation的内存区域都有固定的大小随着新的对象陆续被分配到此区域当这些对象的大小快达到阀门值时就会触发gc操作。通常情况下gc操作发生时所有线程都是暂停的。 如何查看本机heap size :ActivityManager manager(Activity)getSystemService(Context.ACTIVITY_SERVICE); int heapsizemanager.getMemoryClass(); 应用切换操作 Android系统不会再用户切换应用的时候进行交换内存的操作而是把不包含Foreground组件的应用进程放到LRUCache中比如用户启动一个应用系统会为它创建一个进程但是当用户离开这个应用此进程不会被立即销毁而是会放到一个Cache中当用户切换回来够快速的恢复。 发生OOM的条件 通过不同的内存分配方式对不同的对象(bitmap,etc)进行操作因Android版本差异发生变化。4.0以上废除了external的计数器类似bitmap的分配改到dalvik的Java heap(堆)中申请只要allocated新分配的内存getMemoryClass()就会发生OOM。(在AS memory monitor查看内存中Dalvik Heap的实时变化) 如何避免OOM 减少OOM的第一步就是要尽量减少新分配出来的对象占用内存的大小尽量使用更加轻量的对象。 使用更加轻量的数据结构 考虑使用ArrayMap/SpareseArray而不是传统的HashMap等数据结构Android系统为移动系统设计的容器ArrayMap更加高效占用内存更少因为HashMap需要一个额外的实例对象来记录Mapping的操作。而SparesArray高效的避免了key和value的自动装箱而且避免了装箱后的解箱。 避免在Android中使用Enum 减少Bitmap对象的内存占用 Bitmap是一个消耗内存的大胖子减少创建出来的Bitmap的内存占用很重要。一般有两种措施 : 1、inSampleSize:缩放比例在把图片载入内存之前我们需要计算一个合适的缩放比例避免不必要的大图载入。 2、decode format:解码格式选择ARGB_8888/RBG_565/ARGB_4444/ALPHA_8存在很大差异。 使用更小的图片 在设计图片资源的时候我们要考虑图片是否存在可以压缩的空间是否能使用更小的图片使用小图在xml加载资源时就不会在初始化视图因为内存不足而发生InflationException,其根本原因就是发生了OOM。 内存对象的重复利用 Android最常用的缓存算法LRU(Least Recently Use) 复用系统自带的资源比如字符串、图片、动画、样式、颜色、简单布局在应用中直接引用减少自身负重、apk大小、减少内存的开销、复用性更好。但需要考虑版本差异。 Listview和GirdView出现大量重复子组件的视图里面对ConvertView的复用。 Bitmap对象的复用 在ListView和GridView等显示大量图片的控件里面需要使用LRU机制缓存Bitmap. 利用inBitmap的高级特性提高Android系统在Bitmap分配和释放执行效率inBitmap属性可以告知Bitmap解码器使用已经存在的内存区域而不是重新申请一块内存区域存放Bitmap,也就是新解码的Bitmap会使用之前那张bitmap在heap占用的内存区域即使是上千张图片也只占用屏幕能放下图片的内存 SDK19以后新申请的BItmap大小必须小于或等于前面赋值过的bitmap的大小 新的Bitmap和原来的解码格式要相同我们可以创建包含多种类型可以重用的bitmap对象池这样后序的bitmap创建就可以找到合适的模板去重用。 避免在onDraw方法里面执行对象的创建 在onDraw这种频繁调用的方法要避免对象的创建操作因为他会迅速增加内存的使用引起频繁的gc甚至内存抖动 StringBuilder 如果代码中有大量字符串拼接操作使用StringBuilder代替 避免对象的内存泄露 内存对象的泄露会导致不再使用的对象无法及时释放不仅浪费了宝贵的内存空间后续要分配内存的时候空间不足造成OOM。这样每级的generation会变小gc更加容易触发引起内存抖动带来性能问题。 LeakCanary开源控件可以帮助我们发现内存泄露的问题。 Activity泄露是内存泄露最为严重的问题涉及内存多影响面广 . 内部类引用导致Activity的泄露 典型的是Handler导致的Activity泄露如果Handler中有延迟的任务或者等待执行的任务队列过长很可能因为Handler继续执行造成Activity的泄露。 引用链是Looper-MessageQueue-Message-handler-Activity,解决办法是在退出UI之前执行 remove Handler消息队列中的消息与runnable对象。或者使用StaticWeakReference的方式来判断Handler和Activity之间存在引用关系。Activity Context被传递到其他实例中可能导致自身被引用而发生泄露 . 考虑使用Application Context而不是Activity Context 除必须使用Activity Context的情况(Dialog的context必须是Activity),我们可以使用Application Context来避免Activity泄露 注意临时Bitmap的及时回收 大多数情况下我们对Bitmap对象增加缓存机制但是有时候部分bitmap需要及时回收。比如我们临时创建的摸个相对大的bitmap对象变换得到新的bitmap对象后尽快回收原始的bitmap,及时释放原来的空间。 注意监听器的注销 android程序里面register后要及时释放unregister那些监听器自己手动add的listener要记得remove这个listener. 注意缓存容器的对象泄露 有时候我们为了提高对象的复用性把某些对象放到缓存容器中如果这些对象没有及时从容器中清楚也可能导致内存泄露. 注意webview的泄露 Android不同版本对webview产生有很大差异较为严重的问题是webview的泄露解决办法为webview新开一个进程通过AIDL与主进程通信根据业务的需要在合适的时机进行销毁从而达到内存的释放。 内存使用策略优化 谨慎使用large heap android设备由于软硬件的差异heap阀值不同特殊情况下可以在manifest中使用largeheaptrue声明一个更大的heap空间使用getLargeMemoryClass()来获取到这个更大的空间。但是要谨慎使用因为额外的空间会影响到系统整体的用户体验并且会使每次gc的运行时间更长。切换任务时性能大打折扣large heap并不一定能获取到更大的heap. 综合考虑设备内存阈值与其他因素设计合适的缓存大小 例如在设计ListView或者GridView的Bitmap LRU缓存的时候需要考虑的点有 1、应用程序剩下了多少可用的内存空间? 2、有多少图片会被一次呈现到屏幕上有多少图片需要事先缓存好以便快速滑动时能够立即显示到屏幕 3、设备的屏幕大小与密度是多少? 一个xhdpi的设备会比hdpi需要一个更大的Cache来hold住同样数量的图片。 4、不同的页面针对Bitmap的设计的尺寸与配置是什么大概会花费多少内存 5、页面图片被访问的频率是否存在其中的一部分比其他的图片具有更高的访问频繁如果是也许你想要保存那些最常访问的到内存中或者为不同组别的位图(按访问频率分组)设置多个LruCache容器。 6、onLowMemory() 与onTrimMemory() : Android可以在不同的应用当中随意切换。为了让background转到foreground, 每一个background都会占用一定的内存。系统会根据内存的使用情况决定回收部分background的应用内存。background的应用从暂停状态恢复到foreground比较快如果从kill状态恢复比较慢。 资源文件需要选择合适的文件夹进行存放 我们知道hdpi/xhdpi/xxhdpi等等不同dpi的文件夹下的图片在不同的设备上会经过scale的处理。例如我们只在hdpi的目录下放置了一张100100的图片那么根据换算关系xxhdpi 的手机去引用那张图片就会被拉伸到200200。需要注意到在这种情况下内存占用是会显著提高的。对于不希望被拉伸的图片需要放到assets或者nodpi的目录下。 Try catch某些大内存分配的操作 在某些情况下我们需要事先评估那些可能发生OOM的代码对于这些可能发生OOM的代码加入catch机制可以考虑在catch里面尝试一次降级的内存分配操作。例如decode bitmap的时候catch到OOM可以尝试把采样比例再增加一倍之后再次尝试decode。 谨慎使用static对象 因为static的生命周期过长和应用的进程保持一致使用不当很可能导致对象泄漏在Android中应该谨慎使用static对象。 特别留意单例对象中不合理的持有 虽然单例模式简单实用提供了很多便利性但是因为单例的生命周期和应用保持一致使用不合理很容易出现持有对象的泄漏。 珍惜Services资源 如果你的应用需要在后台使用service除非它被触发并执行一个任务否则其他时候Service都应该是停止状态。 另外需要注意当这个service完成任务之后因为停止service失败而引起的内存泄漏。 当你启动一个Service系统会倾向为了保留这个Service而一直保留Service所在的进程。 这使得进程的运行代价很高因为系统没有办法把Service所占用的RAM空间腾出来让给其他组件另外Service还不能被Paged out。 这减少了系统能够存放到LRU缓存当中的进程数量它会影响应用之间的切换效率甚至会导致系统内存使用不稳定从而无法继续保持住所有目前正在运行的service。 建议使用IntentService它会在处理完交代给它的任务之后尽快结束自己。 优化布局层次减少内存消耗 越扁平化的视图布局占用的内存就越少效率越高。我们需要尽量保证布局足够扁平化当使用系统提供的View无法实现足够扁平的时候考虑使用自定义View来达到目的。 谨慎使用“抽象”编程 很多时候开发者会使用抽象类作为”好的编程实践”因为抽象能够提升代码的灵活性与可维护性。然而抽象会导致一个显著的额外内存开销他们需要同等量的代码用于可执行那些代码会被mapping到内存中因此如果你的抽象没有显著的提升效率应该尽量避免他们。 使用nano protobufs序列化数据 Protocol buffers是由Google为序列化结构数据而设计的一种语言无关平台无关具有良好的扩展性。类似XML却比XML更加轻量快速简单。如果你需要为你的数据实现序列化与协议化建议使用nano protobufs。 谨慎使用依赖注入框架 使用类似Guice或者RoboGuice等框架注入代码在某种程度上可以简化你的代码。 谨慎使用多进程 使用多进程可以把应用中的部分组件运行在单独的进程当中这样可以扩大应用的内存占用范围但是这个技术必须谨慎使用绝大多数应用都不应该贸然使用多进程一方面是因为使用多进程会使得代码逻辑更加复杂另外如果使用不当它可能反而会导致显著增加内存。当你的应用需要运行一个常驻后台的任务而且这个任务并不轻量可以考虑使用这个技术。 一个典型的例子是创建一个可以长时间后台播放的Music Player。如果整个应用都运行在一个进程中当后台播放的时候前台的那些UI资源也没有办法得到释放。类似这样的应用可以切分成2个进程一个用来操作UI另外一个给后台的Service。 剔除不需要的代码 使用ProGuard来剔除不需要的代码ProGuard能够通过移除不需要的代码重命名类域与方法等等对代码进行压缩优化与混淆。使用ProGuard可以使得你的代码更加紧凑这样能够减少mapping代码所需要的内存空间。 谨慎使用第三方libraries 很多开源的library代码都不是为移动网络环境而编写的如果运用在移动设备上并不一定适合。即使是针对Android而设计的library也需要特别谨慎特别是在你不知道引入的library具体做了什么事情的时候。例如其中一个library使用的是nano protobufs, 而另外一个使用的是micro protobufs。这样一来在你的应用里面就有2种protobuf的实现方式。这样类似的冲突还可能发生在输出日志加载图片缓存等等模块里面。另外不要为了1个或者2个功能而导入整个library如果没有一个合适的库与你的需求相吻合你应该考虑自己去实现而不是导入一个大而全的解决方案。 设计风格 优化 设计风格很大程度上会影响到程序的内存与性能相对来说如果大量使用类似Material Design的风格不仅安装包可以变小还可以减少内存的占用渲染性能与加载性能都会有一定的提升。 总结 内存优化并不就是说程序占用的内存越少就越好如果因为想要保持更低的内存占用而频繁触发执行gc操作在某种程度上反而会导致应用性能整体有所下降这里需要综合考虑做一定的权衡。 Android的内存优化涉及的知识面还有很多内存管理的细节垃圾回收的工作原理如何查找内存泄漏等等都可以展开讲很多。OOM是内存优化当中比较突出的一点尽量减少OOM的概率对内存优化有着很大的意义。