精华内容
下载资源
问答
  • 优化一下内存
    千次阅读
    2021-11-06 15:49:23

       遇到问题:

            游戏服务器的怪物数据monsterdata.py 用字典表示,怪物字典数据基本做取值和修改处理,不额外增加字段,py文件大小100m,想办法节省内存,根据特点,不使用原生的dict数据结构,用ConstDict是去代替,内存节省到50m左右。

    ConstDict

    python代替的dict数据结构

    若字典不会增加字段,只读/原字段修改
    使用ConstDict可节省内存

    dict()内存主要消耗的地方:

    1、dict扩容机制,预留内存空间
    2、dict也是一个对象,内部会动态维护__dict__,增加slot类属性可以节省内容

    节省内存大小:一半左右,字段越大节省越多

    适用场景:需要生成大量的静态字典场景

    缺点:根据字典的属性,需要先生成类,再生成对象

    python版本:python2.7

    例子代码:

    >>> test_dic = {
    ...     "m_HP": 1,
    ...     "m_MP": 2,
    ...     "m_Attack": 3,
    ...     "m_Defense": 4,
    ...     "m_Speed": 5,
    ...     "m_Dodge": 6,
    ...     "m_Hit": 7,
    ...     "m_Double": 8,
    ... }
    >>>
    >>> class MonsterDict(constdict.ConstDict):
    ...     __slots__ = test_dic.keys()
        
    
    >>> const_dic = MonsterDict(test_dic)
    >>> print(asizesof(test_dic))
    (1192,)
    >>> print(asizesof(const_dic))
    (584,)
    
    
    >>> print(const_dic)
    {'m_Defense': 4, 'm_Speed': 5, 'm_Hit': 7, 'm_Double': 8, 'm_Attack': 3, 'm_HP': 1, 'm_Dodge': 6, 'm_MP': 2}
    
    
    >>> print(const_dic.keys())
    ['m_HP', 'm_Defense', 'm_Speed', 'm_Attack', 'm_Dodge', 'm_MP', 'm_Hit', 'm_Double']
    
    
    >>> print(const_dic.values())
    [1, 4, 5, 3, 6, 2, 7, 8]
    
    
    >>> const_dic["m_Dodge"] = 12456
    >>> print(const_dic["m_Dodge"])
    12456
    
    
    >>> print(const_dic.iteritems())
    <generator object <genexpr> at 0x00000000094FA2C8>
    

    ConstDict.py 代码:

    # -*- coding: utf-8 -*-
    
    
    class ConstDict(object):
        def __init__(self, dic):
            for key, val in dic.iteritems():
                setattr(self, key, val)
    
        def __iter__(self):
            return iter(self.__slots__)
    
        def __getitem__(self, item):
            return getattr(self, item, None)
    
        def __setitem__(self, key, value):
            return setattr(self, key, value)
        
        def __contains__(self, item):
            try:
                return getattr(self, item, False)
            except:
                return False
            
        def get(self, key, default):
            return getattr(self, key, default)
    
        def iteritems(self):
            return ((key, getattr(self, key, None)) for key in self.__slots__)
    
        def items(self):
            return [(key, getattr(self, key, None)) for key in self.__slots__]
    
        def iterkeys(self):
            return iter(self.__slots__)
    
        def itervalues(self):
            return (getattr(self, key, None) for key in self.__slots__)
    
        def keys(self):
            return self.__slots__
    
        def values(self):
            return [getattr(self, key, None) for key in self.__slots__]
        
        def update(self, dic):
            """
            const dict noly support exist keys update
            """
            for key, val in dic.iteritems():
                if getattr(self, key, False):
                    setattr(self, key, val)
            raise NotImplementedError("ConstDictBase not support update")
        
        def __str__(self):
            return str(dict(self.items()))
    

    代码优化维护会在gitbub更新

    github:GitHub - Grente/ConstDict: python节省内存的字典数据结构

    更多相关内容
  • iOS性能优化内存

    千次阅读 2020-12-18 23:03:26
      之前项目开发过程中也有对内存进行优化,但是并没有进行系统的优化,更多是隔一段时间优化一些。最近自己总结了一些自己的心得体会分享给大家,希望大家能够多多批评指正。 app内存优化方向 降低运行内存峰值 1...

      之前项目开发过程中也有对内存进行优化,但是并没有进行系统的优化,更多是隔一段时间优化一些。最近自己总结了一些自己的心得体会分享给大家,希望大家能够多多批评指正。

    app内存优化方向

    降低运行内存峰值

    1、减少不必要单例的使用,尽可能减少单例对占内存较大对象的强持有
    2、排查内存泄漏
    3、降低图片缓存工具SDWebImage最大缓存值,保证必要的流畅性
    4、大图尽量不要使用[UIImage imageNamed:@“”],(ps:系统会进行缓存)
    5、在整个app运行期间不销毁的对象能够通过一定的类似归档的机制,进行快速的恢复与创建,比如tabbar上的页面,切换的到某个页面时,其他几个tab上的页面进行归档,切换回来的时候进行恢复。(ps:要根据事情情况决定,避免反复创建与销毁)
    6、在条件允许的情况下,尽可能降低图片的品质

    降低运行期间内存峰值的波动

    1、对较大数据操作时,通过stream的形式进行,具体场景如下:
      1)上传文件的时候
      2)下载文件的时候(下载的时候,也可以通过将数据写进文件的形式)
      3)socket传输文件的时候
      4)本地导入大文件的时候,比如对大文件进行md5运算的时候
    2、加载大图的时候采取优化措施(ps:具体可以搜下,网上相关的博客很多,这里不再一一列出)
    3、及时释放不再使用的对象
      1)对于业务中存在循环创建的逻辑,要及时释放不再需要的,比如电商app存在一个循环流程:商详页->其他页面->商详页->其他页面。应该进行限制,比如循环了几次后需要把中间的某些页面给释放掉,避免内存一直增加。
      2)循环体内创建的占内存较大的对象通过autoreleasepool及时释放
      3)app没有打开的RN页面时,及时把RN的bridge释放掉。(ps:这个bridge很占内存)

    4,内存调度策略
    1、 在开辟一块大内存之前,及时释放一些内存,避免同时出现持有多个占用内存较大的对象.比如在打开RN页面时,SDWebImage清理下缓存中的图片,将一些可以恢复的对象暂时销毁掉等。
    2、根据长期观察的结果,结合机型对不同的手机采取不同的内存调度策略。

    app内存优化的阶段

    app收到内存警告前进行优化

      在合理的范围内,尽最大可能降低app运行内存峰值,降低app运行内存峰值的波动

    app收到内存警告后进行优化

      以最快的速度降低内存峰值,防止app被系统杀死。可以根据剩余内存、收到内存警告的次数、机型等一些额外信息制定内存释放的等级。在必要的时候可以让不重要的业务作出牺牲。

    更多干货文章,扫描下方二维码关注公众号
    在这里插入图片描述

    展开全文
  • Android 性能优化--内存

    千次阅读 2021-07-06 23:15:05
    Android 性能优化内存篇 Android内存优化是我们性能优化工作中比较重要的一环,主要包括两方面的工作: 优化RAM,即降低运行时内存。目的是防止程序发生OOM异常,以及降低程序由于内存过大被LowMemoryKiller(LMK)...

    Android内存优化是我们性能优化工作中比较重要的一环,主要包括两方面的工作:

    1. 优化RAM,即降低运行时内存。目的是防止程序发生OOM异常,以及降低程序由于内存过大被LowMemoryKiller(LMK)机制杀死的概率。同时,不合理的内存使用会使GC次数大大增多,从而导致程序变卡。
    2. 优化ROM,即降低程序占ROM的体积,防止ROM空间不足导致程序无法安装等问题。

    前言

    Android中关于内存优化的问题主要包括三个方面:

    • Memory Leaks 内存泄漏
    • OutOfMemory 内存溢出
    • Memory Churn 内存抖动

    同时,和内存相关的三个主要数据为:

    • 总内存
    • 系统可用内存
    • 当前App可用内存

    扩大内存

    1. 申请更大内存

    每一个Android设备都会有不同的RAM总大小与可用空间,因此不同设备为App提供了不同大小的heap限制,可以通过调用getMemoryClass()来获取当前App的可用heap大小。

    在一些特殊情景下,可以通过在manifest的application标签下添加largeHeap=true属性来声明一个更大的heap空间,之后可以通过getLargeMemoryClass()来获取到一个更大的heap大小。但这不是该值得提倡的方法,因为使用额外的内存会影响系统整体的用户体验,并且会使得GC的运行时间更长。在任务切换时,系统的性能会变得大打折扣。

    而且, large heap并不一定能够获取到更大的heap。在某些有严格限制的机器上,large heap的大小和通常的heap size是一样的。因此即使你申请了large heap,你还是应该通过执行getMemoryClass()来检查实际获取到的heap大小。

    1. 创建多进程

    内存泄漏

    对象由于编码错误或系统原因,仍然存在着对其直接或间接的引用,导致系统无法进行回收。
    内存泄漏引发的常见问题有:

    • 应用卡顿,响应速度慢(内存占用高时JVM虚拟机会频繁触发GC);
    • 应用被从后台进程处理为空进程;
    • 应用莫名崩溃(引起OOM);

    常见泄漏类型

    单例模式导致的内存泄漏

    使用单例持有Context,需要记得释放,或使用全局上下文。

    静态变量导致的内存泄漏

    除了避免显示地使用静态变量引用拥有自己生命周期的对象外,也需要注意一些隐式的使用,如下:

    private static Drawable sBackground;
    @Override
    protected void onCreate(Bundle state) {
        super.onCreate(state);
        TextView label = new TextView(this);
        label.setText("Leaks are bad");
        if (sBackground == null) { 
            sBackground = getDrawable(R.drawable.large_bitmap);
        }
        label.setBackgroundDrawable(sBackground);
        setContentView(label);
    }
    

    sBackground是一个静态的变量,我们虽然没有显式地保存Context的引用,但是,当Drawable与View连接之后,Drawable就将View设置为一个回调,由于View中是包含Context的引用的,所以,实际上我们依然保存了Context的引用。这个引用链如下:Drawable->TextView->Context
    所以,最终该Context也没有得到释放,发生了内存泄露。

    解决方案

    • 尽量避免静态变量引用资源耗费过多的对象,如Context,同时注意对静态对象的释放。
    • Context尽量使用ApplicationContext。
    • 使用WeakReference代替强引用,比如可以使用WeakReference mContextRef;

    非静态内部类导致的内存泄漏

    尽量不要在Activity中使用非静态内部类(内部类/匿名类),因为非静态内部类会隐式持有外部类实例的引用(具体可以查看细话Java:“失效”的private修饰符了解)。可使用静态内部类 + WeakReference代替内部类,如Handler、线程、AsyncTask、Timer等,或者在销毁时主动移除引用,如在Activity的onDestroy中调用handler.removeCallbacksAndMessages取消延时消息(如:匿名Runnable被发送到MessageQueue中而导致泄漏)。

    使用线程未及时释放

    子线程(如在回调中)引用了某个对象,如activity,activity销毁时线程仍未结束,此时便会导致activity泄漏。可在destroy时对线程进行销毁。

    使用资源未及时关闭导致的内存泄漏

    由于编码问题,使用结束后未对资源进行销毁,虽然这些资源最终会在finalize中关闭,但得不到及时释放便会造成内存泄露。故需要及时处理,如:Bitmap.recycle(),Cursor.close,inputStream.close()(IO),socket(网络).close(),数据库关闭等。

    容器对象泄漏(注册对象未及时注销)

    监听器(包括系统服务)/广播等的register & unregister

    属性动画导致的内存泄漏

    onDestroy时调用动画的cancel方法取消属性动画

    WebView导致的内存泄漏

    使用WebView,在Activity.onDestory需要移除和销毁,webView.removeAllViews()和webView.destory(),但最好是将WebView置于另一进程,需要销毁时杀死该进程。

    大体积对象的及时回收–bitmap/webView/Cursor

    onTrimMemory()与onLowMemory()

    检测工具

    Android Profiler(Android Studio自带)

    可以检测CPU, Memory和Network三个方面的性能问题。

    LeakCanary(三方)

    通过弱引用方式侦查Activity或对象的生命周期,若发现内存泄露自动dump
    Hprof文件,通过HAHA库得到泄露的最短路径,最后通过notification展示。

    内存泄露判断与处理的流程如下图 ,各自运行的进程空间(主进程通过idlehandler,HAHA分析使用的是单独的进程):

    TraceView

    Traceview是Android平台特有的数据采集和分析工具,主要用于分析Android中应用程序的hotspot(瓶颈)。Traceview本身只是一个数据分析工具,而数据的采集则需要使用Android SDK中的Debug类或者利用DDMS工具。二者的用法如下:

    • 开发者在一些关键代码段开始前调用Android SDK中Debug类的startMethodTracing函数,并在关键代码段结束前调用stopMethodTracing函数。这两个函数运行过程中将采集运行时间内该应用所有线程(注意,只能是Java线程)的函数执行情况,并将采集数据保存到/mnt/sdcard/下的一个文件中。开发者然后需要利用SDK中的Traceview工具来分析这些数据。
    • 借助Android SDK中的DDMS工具。DDMS可采集系统中某个正在运行的进程的函数调用信息。对开发者而言,此方法适用于没有目标应用源代码的情况。DDMS工具中Traceview的使用如下:
    1. 在启动待分析事件前,在DDMS–Name窗口中选择对应程序包名,此时该窗口的顶部会出现目标进程的数据采集按钮,点击该按钮;
    2. 开始执行对应事件;
    3. 执行结束后按下刚才的采集数据按钮结束采集,此时DDMS会自动触发Traceview来浏览采集的数据;
    4. 在Traceview窗口里下方的窗口中找到对应方法所在的行,便可以查看对应方法执行过程中的各个参数。具体参数意义如下:
    Name 该线程运行过程中所调用的函数名
    Incl Cpu Time 某函数占用的CPU时间,包含内部调用其它函数的CPU时间
    Excl Cpu Time 某函数占用的CPU时间,但不含内部调用其它函数所占用的CPU时间
    Incl Real Time 某函数运行的真实时间(以毫秒为单位),内含调用其它函数所占用的真实时间
    Excl Real Time 某函数运行的真实时间(以毫秒为单位),不含调用其它函数所占用的真实时间
    Call+Recur Calls/Total 某函数被调用次数以及递归调用占总调用次数的百分比
    Cpu Time/Call 某函数调用CPU时间与调用次数的比。相当于该函数平均执行时间
    Real Time/Call 同CPU Time/Call类似,只不过统计单位换成了真实时间
    

    heap(DDMS内)

    heap工具可以帮助我们检查代码中是否存在会造成内存泄漏的地方,使用步骤如下:

    1. 启动eclipse后,切换到DDMS透视图,并确认Devices视图、Heap视图都是打开的;
    2. 点击选中想要监测的进程,比如system_process进程;
    3. 点击选中Devices视图界面中最上方一排图标中的“Update Heap”图标;
    4. 点击Heap视图中的“Cause GC”按钮;
    5. 此时在Heap视图中就会看到当前选中的进程的内存使用量的详细情况。

    说明:

    • 点击“Cause GC”按钮相当于向虚拟机请求了一次gc操作;
    • 当内存使用信息第一次显示以后,无须再不断的点击“Cause GC”,Heap视图界面会定时刷新,在操作过程中就可以看到内存使用的变化。

    Heap视图中部有一个Type叫做data object,即数据对象,也就是我们的程序中大量存在的类类型的对象。在data object一行中有一列是“Total Size”,其值就是当前进程中所有Java数据对象的内存总量,一般情况下,这个值的大小决定了是否会有内存泄漏。可以这样判断:

    • 不断操作当前应用,同时注意观察data object的Total Size值;
    • 正常情况下Total Size值都会稳定在一个有限的范围内,也就是说由于程序中的的代码良好,没有造成对象不被垃圾回收的情况,所以说虽然我们不断的操作会不断的生成很多对象,而在虚拟机不断的进行GC的过程中,这些对象都被回收了,内存占用量会落到一个稳定的水平;
    • 反之如果代码中存在没有释放对象引用的情况,则data object的Total Size在每次GC后不会有明显的回落,随着操作次数的增多Total Size的值会越来越大,直到到达一个上限后导致进程被kill掉。

    allocation tracker

    运行DDMS,选择应用进程,单击Allocation tracker标签,在打开的窗口中单击“Start Tracing”按钮,让应用运行你想分析的代码。运行完毕后,单击“Get Allocations”按钮,一个已分配对象的列表就会出现在第一个表格中。单击第一个表格中的任何一项,在表格二中就会出现导致该内存分配的栈跟踪信息。通过allocation tracker,不仅知道分配了哪类对象,还可以知道在哪个线程、哪个类、哪个文件的哪一行。

    StrictMode

    主要有两种策略,既可用于作IO相关的代码优化,也可用于内存优化。

    1. 线程方面策略(TreadPolicy)
      线程策略主要用于检测UI线程中是否存在读写磁盘的操作,是否有网络请求操作,以及检查自定义代码是否在UI线程执行得比较慢的情况。
    • 自定义的耗时调用:使用detectCustomSlowCalls()开启
    • 磁盘读取操作:使用detectDiskReads()开启
    • 磁盘写入操作:使用detectDiskWrites()开启
    • 网络操作:使用detectNetwork()开启
    1. VM方面策略(VmPolicy)
      VmPolicy策略主要用于发现内存问题,比如Activity内存泄漏,SQL对象内存泄漏,IO操作对象资源未释放。
    • detectActivityLeaks()
    • detectLeakedClosableObjects()
    • detectLeakedSqlLiteObjects()
    • setClassInstanceLimit():检测实例数量

    只要主线程中配置了并启动,它就能监听主线程的运行情况,当发现有重大问题时和违背策略的时候,就会以logcat的形式提示用户。

    内存溢出

    原因:申请内存超过应用最大内存,一般都是内存泄漏导致可用内存不足,或申请内存过大,或创建对象太过频繁。

    1. 内存泄漏导致内存不够(内存泄漏原因看上面)

    2. 对象内存过大问题

    保存了多个耗用内存过大的对象(如Bitmap、XML文件),造成内存超出限制,所以Bitmap对象不再使用时要调用Bitmap.recycle()释放内存。避免图片过大导致OOM的方法有:

    • 尽量不要使用setImageBitmap或setImageResource或BitmapFactory.decodeResource来设置一张大图,因为这些函数在完成decode后,最终都是通过java层的createBitmap来完成的,需要消耗更多内存,可以通过BitmapFactory.decodeStream方法,创建出一个bitmap,再将其设为ImageView的source。
    • 按需加载Bitmap,适当降低Bitemap的采样率(inSampleSize)或解码率(inPreferredConfig),如:
    BitmapFactory.Options options = new BitmapFactory.Options();
    options.inSampleSize = 2;//图片宽高都为原来的二分之一,即图片为原来的四分之一
    options.inPreferredConfig = Bitmap.Config.ARGB_565; // 降低解码率
    Bitmap bitmap = BitmapFactory.decodeStream(cr.openInputStream(uri), null, options);
    preview.setImageBitmap(bitmap);
    
    • 及时销毁,如Bitmap.recycle()或使用Java软/弱引用,进行图片缓存(LruCache),将需要经常加载的图片放进缓存里,避免反复加载,及时销毁不再使用的Bitmap对象。
    1. 不要在执行频率很高的方法中创建对象(或局部变量)(比如onMeasure、循环),可以使用HashTable等创建一组对象容器从容器中取那些对象,而不用每次new与释放。同时可以使用线程池管理线程,提高线程利用率,降低线程创建开销。

    2. 避免代码设计模式的错误造成内存泄露;譬如循环引用,A持有B,B持有C,C持有A,这样的设计谁都得不到释放,不过如果虚拟机使用的是可达性分析则可以回收循环引用。

    3. ListView使用Adapter时,没有使用系统缓存的convertView。在getView方法中若没有使用convertView缓存,listview会频繁实例化新的view对象,不但浪费资源又 浪费时间,而且会占用大量的内存。

    4. 界面切换导致OOM

    有时横竖屏切换N次后会OOM,这种问题没有固定的解决方法,但可以从以下几个方面分析。

    • 查看页面布局中有没有大图,如背景图之类的。去除xml中相关设置,改在程序中设置背景图(放在onCreate()方法中):
    Drawable drawable = getResources().getDrawable(R.drawable.id);
    ImageView imageView = new ImageView(this);
    imageView.setBackgroundDrawable(drawable);
    

    在Activity destory时注意,drawable.setCallback(null);, 防止Activity得不到及时的释放

    • 跟上面方法相似,直接把xml配置文件加载成view再放到一个容器里,然后直接调用this.setContentView(View view);方法,避免xml的重复加载。
    • 在页面切换时尽可能少地重复使用一些代码,比如:重复调用数据库,反复使用某些对象等等。

    OOM能否try catch?

    可以,但只能catch到try里面发生的OOM,当然这不是一个好的做法,正确的做法依然是通过减少内存泄漏和缩小对象体积。

    5R法则:

    • Reduce
      差异化对待:不同机型设置不同缓存容量,图片资源;
      按需加载
      减少bitmap对象的内存占用–insamplesize,decode format(ARGB_8888:32bit per pixel, 444: 16, 565: 16, Alpha_8: 8)
    • Reuse
      复用池:LRU
      bitmap对象复用:inBitmap
      listView/GripView中ConverView复用
      避免频繁创建和回收对象:draw(内存抖动)
    • Recycle
      内存泄漏
    • Refactor
      使用优化过的数据结构
      减少内存碎片化
      优化布局,减少内存消耗(过度绘制)
    • Revalue
      谨慎使用largeHeap
      谨慎使用多进程
      谨慎使用第三方libaries

    Dump命令(adb shell dumpsys meminfo packagename)
    android studio

    常见问题与优化
    使用合适的Context,一般注册第三方框架或者SDK时采用Application的Context,除非特地要求传Activity的Context。

    多进程应用需要避免Application onCreate多次执行引起的重复初始化(pid校验)

    图片资源放对位置(优先考虑主流设备,优先考虑用户分布多的设备)

    图片加载:
    inSampleSize(降低采样率)
    BitmapRegionDecoder(加载超级大图)
    Matrix(小图放大)
    LruCache/LinkedHashMap(缓存控制,避免OOM)
    选择合适的图片加载框架(UIL、Fresco、Glide、Picasso)
    按需显示,优先缩略图,需要时再显示大图
    优化加载图片的时机

    主动释放内存:
    关键函数:Activity.onTrimMemory (int level)
    TRIM_MEMORY_UI_HIDDEN
    TRIM_MEMORY_RUNNING_MODERATE
    TRIM_MEMORY_RUNNING_LOW
    TRIM_MEMORY_RUNNING_CRITICAL
    TRIM_MEMORY_BACKGROUND
    TRIM_MEMORY_MODERATE
    TRIM_MEMORY_COMPLETE
    选择合适的时机,资源和View解绑,在合适的时机再回复
    释放缓存

    小心未关闭的Dialog,在Activity或者Fragment销毁时记得先dismiss

    匿名内部类和非静态内部类泄漏,用静态外部类和弱引用方式取而代之

    避免创建大量的临时对象而造成内存抖动,考虑复用。

    高频执行函数中避免创建大量临时对象。如View的onDraw和onTouch等
    StringBuilder、StringBuffer代替String

    WebView造成内存泄漏
    Android系统和各家的ROM本身存在的问题造成的,最好的处理方式是将承载WebView的界面独立到其他进程,在合适的时机选择杀死WebView进程。

    自身内存监控(来自腾讯开发者提供的一种方案)
    Runtime.getRuntime().maxMemory();
    Runtime.getRuntime().totalMemory() - Runtime.getRuntime().freeMemory();

    定期检查上边的比例值,达到一定峰值时调用此API触犯内存的释放WindowManagerGlobal.getInstance().startTrimMemory(TRIM_MEMORY_COMPLETE);

    原理参见:http://t.cn/RfUC2ft

    更多
    注册和反注册(BroadcastReceiver、Observer)
    关闭资源(Cursor、IO流)
    使用优化过的数据结构SparseXXXX
    考虑使用Parcelable取代Serializable
    建立缓存池,如ListView复用View的思路
    开辟多进程(当然,也不是越多越好)
    借助lint来规避一些常见的编码问题

    展开全文
  • App性能优化内存优化

    千次阅读 2020-01-03 14:22:41
    App性能优化内存优化 本篇主要探讨app性能优化相关的知识点,预计20年2月底前完成 内存优化工具 内存管理机制 内存都懂解决 内存泄漏解决 MAT详解 小结 ...

    App性能优化:内存优化

    目录:

    1. 内存优化工具
    2. 内存管理机制
    3. 内存抖动解决
    4. 内存泄漏解决
    5. MAT详解
    6. 小结
    背景介绍
    • 内存是大问题但缺乏关注
    • 压死骆驼的最后一根稻草
    表现形式
    • 内存抖动:锯齿状,GC导致卡顿
    • 内存泄漏:可用内存减少、频繁GC
    • 内存溢出:OOM、程序异常

    1、工具选择

    • Memory Profiler
    • Memory Analyzer
    • LeakCanary
    Memory Profiler
    1. 实时图标展示内存使用量
    2. 识别内存泄漏、抖动
    3. 提供捕获堆转储、强制GC以及跟踪内存分配的能力
    Memory Profiler的使用
    1. 非常直观
    2. 线下平时使用
    Memory Analyzer(MAT)
    1. 强大的Java heap 分析工具,帮助查找内存泄漏和内存占用
    2. 生成整体报告,分析问题
    3. 线下深入使用
    LeakCanary
    1. 自动内存泄漏检测
    2. 线下集成

    2、android内存管理机制

    Java内存管理机制

    在这里插入图片描述

    Java内存回收算法-标记清除算法
    • 标记出所有需要回收的对象
    • 统一回收所有被标记的对象
      在这里插入图片描述
    标记回收算法存在的问题
    • 标记和清除的效率不高
    • 产生大量的内存碎片
    Java内存回收算法之 复制算法
    • 将内存分成两份,只将数据存储在其中一块上。
    • 当需要垃圾回收时,也是标记出废弃的数据,然后将有用数据复制到另一块内存上,最后将第一块内存全部清除
      在这里插入图片描述
    复制算法的问题
    • 实现简单,运行高效
    • 浪费一半空间,代价大
    Java内存回收算法之 标记整理算法
    1. 标记过程与“标记-清除算法”一样
    2. 存活对象往一端移动
    3. 清理其余内存
      在这里插入图片描述
    标记整理算法的特点
    1. 避免标记-清理导致的内存碎片
    2. 避免复制算法的空间浪费
    Java内存回收算法之 分代收集算法
    1. 结合多种收集算法优势
    2. 新生带对象存活率低:复制算法
    3. 老年代对象存活率高:标记-整理算法
    Android内存管理机制
    • 内存弹性分配,分配值和最大值受具体设备影响
    • OOM场景:内存真正不足,可用内存不足
    Dalvik 与Art区别
    • Dalvik仅固定一种回收算法
    • Art回收算法可运行期选择
    • Art具备内存整理能力,减少内存空洞
    Low Memory killer
    • 进程分类:前端、可见、服务、后台、空进程,优先级依次降低
    • 回收收益

    内存抖动案例

    ######制造内存抖动

        companion object {
            var datas: ArrayList<TestClass>? = null
            val handler = object : Handler() {
                override fun handleMessage(msg: Message) {
                    //创造内存抖动
                    for (i in 0..100) {
                        datas = ArrayList<TestClass>(100000)
                    }
                    sendEmptyMessageDelayed(0, 1000)
                }
            }
        }
    

    在这里插入图片描述

    内存抖动解决技巧
    1. 找循环或者频繁调用的地方
    2. 通过Profiler定位,锯齿状或者频繁的GC

    内存泄漏

    • 定义:内存中存在没有用的对象,又无法回收
    • 表现:内存抖动、可用内存逐渐减少

    内存泄漏案例

    1. 静态变量持有Activity引用导致Activity无法被回收
    //定义一个接口
    public interface Callback {
    
        void doOperation();
    }
    //定义持有Activity引用的类
    public class CallbackMananger {
        public static ArrayList<Callback> sCallbacks = new ArrayList();
    
        public static void addCallback(Callback callback) {
            sCallbacks.add(callback);
        }
    
        public static void removeCallBack(Callback callback) {
            sCallbacks.remove(callback);
        }
    }
    
    
    1. 在生成LeakCanaryActivity,显示一张图片
    
    class MemoryLeakActivity : AppCompatActivity(), Callback {
    
        override fun onCreate(savedInstanceState: Bundle?) {
            super.onCreate(savedInstanceState)
            setContentView(R.layout.activity_memory_leak)
            val bitmap = BitmapFactory.decodeResource(resources, R.drawable.icon_leaktest)
            iv.setImageBitmap(bitmap)
            //保存Activity引用
            CallbackMananger.addCallback(this)
        }
    
        override fun doOperation() {
    
        }
    }
    
    
    1. 重复打开关闭 页面,即可在Profiler中看到内存成阶梯状上升,如下图所示:
      在这里插入图片描述
      显然:这里每次GC的的时候并没有导致MemoryLeakActivity被回收,内存图形呈现阶梯状显示。

    2. 使用MAT分析内存泄漏
      1. 进入官网下载MAT工具:MAT下载地址
      2. 下载对应系统版本
      在这里插入图片描述

    3. 安装MAT工具,双击zip文件解压后得到mat.app
      在这里插入图片描述

    4. 获取堆文件 Heap Dump
      在这里插入图片描述

    5. 将文件存到本地
      在这里插入图片描述
      在这里插入图片描述

    6. 注意这里是Android的hprof文件,需要使用android 的platform-tool里面的hprof-conv工具对文件进行一次格式转化,转化命令如下:
      ./hprof-conv /Users/hudingpeng/Desktop/memory-20200221T114049.hprof 2.hprof
      在这里插入图片描述

    7. 这样在platform-tools目录下会生成一个2.hprof文件,如下图所示:
      在这里插入图片描述

    8. 打开MAT工具:

      1. 直接点击mat.app会报错,按照以下步骤运行
      2. 右键mat.app => 显示包内容 =>进入 mat.app/Contents/MacOS ,此目录下就有工具MemoryAnalyzer =>双击直接打开工具就行了
        在这里插入图片描述
    9. 在这里插入图片描述
      10.点击open a Heap Dump选择我们的转化后的文件2.hprof 文件即可,下面分析MAT工具的一些常用用法

    MAT工具分析内存泄漏

    1. 打开hprot文件,选择Histogram
      在这里插入图片描述

    2. 搜索我们的泄漏的MemoryLeakActivity,可以看到有4个MemoryLeakActivity对象,表示已经泄漏了
      在这里插入图片描述

    3. 右键选择查看with incoming references
      在这里插入图片描述

    4. 选择一个,点击查看到GC Root的路径,看到具体引用的地方
      在这里插入图片描述
      在这里插入图片描述
      这样我们就看到了具体泄漏的地方就是,CallbackManager类里面的静态变量sCallbacks

    5. 点击Group by package 可以按包名排序
      在这里插入图片描述
      在这里插入图片描述
      在这里插入图片描述
      这样也可以很方便看到我们的包名下的类对象生成的情况,这里也可以看到MemoryLeakActivity在内存里有4个对象

    6. 第三个菜单项:dominator_tree 视图,显示对象占用内存的百分比
      在这里插入图片描述
      可以看到对象占用内存的百分比,给我们的内存优化提供一定的参考

    7. 第四个菜单项:OQL,相当于一种数据查询语言,我们直接搜索我们的类,点击感叹号查询,如下图所示
      在这里插入图片描述

    8. Top Consumer条目:通过图表的形式显示占用内存最多的对象
      在这里插入图片描述
      在这里插入图片描述
      这对我们的内存占用优化也是一个不错的参考

    关于BitMap的优化

    Btimap的内存模型
    1. 获取Bitmap占用的内存

      1. getByteCount()方法
      2. 宽 x 高 x 一像素占用的内存
    2. 常规方式:
      背景:图片对内存大小至关重要、图片宽高大于控件宽高

    3. 通过Epic ARTHook优雅检测不合理的图片
      Epic:Epic 是一个在虚拟机层面、以 Java Method 为粒度的 运行时 AOP Hook 框架。简单来说,Epic 就是 ART 上的 Dexposed(支持 Android 4.0 ~ 10.0)。它可以拦截本进程内部几乎任意的 Java 方法调用,可用于实现 AOP 编程、运行时插桩、性能分析、安全审计等。
      Epic 被 VirtualXposed 以及 太极 使用,用来实现非 Root 场景下的 Xposed 功能,已经经过了相当广泛的验证。
      非常厉害的AOP框架,参考连接:Epic AOP框架
      这里我们Hook setImageBitmap方法:

    import android.graphics.Bitmap;
    import android.graphics.drawable.BitmapDrawable;
    import android.graphics.drawable.Drawable;
    import android.util.Log;
    import android.view.View;
    import android.view.ViewTreeObserver;
    import android.widget.ImageView;
    
    import com.optimize.performance.utils.LogUtils;
    import com.taobao.android.dexposed.XC_MethodHook;
    
    public class ImageHook extends XC_MethodHook {
    
        @Override
        protected void afterHookedMethod(MethodHookParam param) throws Throwable {
            super.afterHookedMethod(param);
            // 实现我们的逻辑
            ImageView imageView = (ImageView) param.thisObject;
            checkBitmap(imageView,((ImageView) param.thisObject).getDrawable());
        }
    
    
        private static void checkBitmap(Object thiz, Drawable drawable) {
            if (drawable instanceof BitmapDrawable && thiz instanceof View) {
                final Bitmap bitmap = ((BitmapDrawable) drawable).getBitmap();
                if (bitmap != null) {
                    final View view = (View) thiz;
                    int width = view.getWidth();
                    int height = view.getHeight();
                    if (width > 0 && height > 0) {
                        // 图标宽高都大于view带下的2倍以上,则警告
                        if (bitmap.getWidth() >= (width << 1)
                                && bitmap.getHeight() >= (height << 1)) {
                            warn(bitmap.getWidth(), bitmap.getHeight(), width, height, new RuntimeException("Bitmap size too large"));
                        }
                    } else {
                        final Throwable stackTrace = new RuntimeException();
                        view.getViewTreeObserver().addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() {
                            @Override
                            public boolean onPreDraw() {
                                int w = view.getWidth();
                                int h = view.getHeight();
                                if (w > 0 && h > 0) {
                                    if (bitmap.getWidth() >= (w << 1)
                                            && bitmap.getHeight() >= (h << 1)) {
                                        warn(bitmap.getWidth(), bitmap.getHeight(), w, h, stackTrace);
                                    }
                                    view.getViewTreeObserver().removeOnPreDrawListener(this);
                                }
                                return true;
                            }
                        });
                    }
                }
            }
        }
    
    
        private static void warn(int bitmapWidth, int bitmapHeight, int viewWidth, int viewHeight, Throwable t) {
            String warnInfo = new StringBuilder("Bitmap size too large: ")
                    .append("\n real size: (").append(bitmapWidth).append(',').append(bitmapHeight).append(')')
                    .append("\n desired size: (").append(viewWidth).append(',').append(viewHeight).append(')')
                    .append("\n call stack trace: \n").append(Log.getStackTraceString(t)).append('\n')
                    .toString();
    
            LogUtils.i(warnInfo);
        }
    
    }
    
    

    在App onCreate方法里注册要Hook的方法,传入ImageHook

     DexposedBridge.hookAllConstructors(ImageView.class, new XC_MethodHook() {
                @Override
                protected void afterHookedMethod(MethodHookParam param) throws Throwable {
                    super.afterHookedMethod(param);
                    DexposedBridge.findAndHookMethod(ImageView.class, "setImageBitmap", Bitmap.class, new ImageHook());
                }
            });
    

    more:LeakCanary 监控内存泄漏

    内存优化总结

    优化大方向
    1. 内存泄漏
    2. 内存抖动
    3. Btimap
    优化细节
    1. LargeHeap属性 (建议开启,大概率能申请到更多内存)
    2. onTrimMemory (系统块干掉app了,可以手动清理界面,跳转到主界面)
    3. 使用系统优化过的集合:SparseArray
    4. 谨慎使用SharedPreferences
    5. 谨慎使用外部库
    6. 业务架构设计合理
    展开全文
  • 【C++】【内存】系列三:内存优化

    千次阅读 2021-12-12 21:55:25
    程序性能有2个考核指标,一是时间,二是空间,本文是针对空间上进行优化,尽可能减少程序的运行内存(可能会增加运行时间)。 内存优化 架构便于拓展 算法空间上面省 函数inline 单个执行语句for ...
  • Android常用内存优化方式整理

    千次阅读 2022-03-01 15:38:39
    内存优化常用手段前言减小对象内存占用基本数据类型与包装类型枚举Bitmap图片代码混淆序列化对象复用对象池ListView/GridView/RecyclerViewBitmap复用内存泄漏静态变量单例属性动画非静态内部类异步线程/任务Handler...
  • Unity内存优化

    千次阅读 2022-02-11 22:16:14
    Unity内存优化
  • cocos优化内存

    千次阅读 2022-03-11 00:01:58
    onExitTransitionDidStart——>curScene:onExit——>curScene:cleanup——>nextScene:onEnter——>nextScene:onEnterTransitionDidFinish _sceneStack:Director类的Vector 内存优化可在onEnter创建缓存...
  • 提示:本篇博客是个人学习的总结,有什么问题请及时联系本人qq1521976977 ...内存优化一般从两个方向着手优化,一方面就是上篇博客写的防止内存泄漏,避免不必要的内存资源浪费;另一方面就是APP中大对象的优化
  • Redis内存优化

    万次阅读 2020-04-16 14:50:06
    使用maxmemory参数限制最大可用内存,当超出内存上限maxmemory时使用LRU等删除策略释放空间以及防止所用内存超过服务器物理内存。 2.配置内存回收策略 Redis所用内存达到maxmemory上限时会触发相应的溢出控制策略...
  • Cocos Creator性能优化-4-内存优化

    千次阅读 2022-01-15 19:33:25
    1.什么是内存优化
  • ElasticSearch内存优化

    千次阅读 2022-03-27 21:37:06
    ElasticSearch内存优化
  • 今天来聊一聊Android中内存优化的一些手段。 首先问问自己为什么要内存优化呢? (1):App消耗内存过大,导致手机内存低于内存警戒线的时候,Low Memory Killer机制就会触发,App占用内存越多,被处理掉的机会就越...
  • GitLab内存优化

    千次阅读 2022-01-26 11:36:33
    GitLab git 内存优化 优化
  • Unity性能优化内存

    千次阅读 2020-08-09 00:04:27
    本篇文章主要内容摘自UWA的《性能优化,进无止境-内存篇》。地址如下: 性能优化,进无止境-内存篇(上) 性能优化,进无止境---内存篇(下) 本篇主旨是总结两篇内容的重点,细节还请在作者文章查看。 内存优化...
  • Android-APP内存优化

    千次阅读 2018-04-09 14:27:10
    为什么要进行内存优化 APP运行内存限制,OOM导致APP崩溃 APP性能:流畅性、响应速度和用户体验
  • Glide内存占用优化

    千次阅读 2019-10-12 19:01:37
    Glide内存占用优化 近期项目中选用glide作为图片加载工具,布局采用NestedScrollView嵌套RecyclerView实现,recycleview中的条目有张图片,通过glide添加,RecyclerView不断下滑不断加载更多item,随着recycleview中...
  • Linux性能优化-内存性能优化思路

    千次阅读 多人点赞 2019-01-07 08:27:48
    内存优化思路
  • 如何写出高性能代码(三)优化内存回收(GC)

    千次阅读 多人点赞 2022-05-02 09:33:33
      本文是《如何写出高性能代码》系列的第三篇,本文将告诉你如何写出GC更优的代码,以达到提升代码性能的目的 优化内存回收   垃圾回收GC(Garbage Collection)是现在高级编程语言内存回收的主要手段,也是高级...
  • 1 Android内存抖动 内存抖动是指内存忽高忽低,有短时间内快速的上升和下落的趋势,内存呈锯齿状。此时会频繁的GC,造成卡顿,甚至有OOM的可能 内存抖动越剧烈,说明单次分配的内存更大。 2 内存抖动的定位 对于...
  • http://yuerbaobei.ys168.com/,也可以直接网盘下载,网盘有使用方法视频教程,一键式操作,瞬间降低游戏进程的cpu内存暂用率,工作室挂机多开游戏必备。淘宝指定ID:总店:玉儿宝贝7925590-----分店:玉儿宝贝商城 ...
  • c++的内存优化

    千次阅读 2019-01-18 17:43:33
    2.内存优化有哪些方式 3.怎样做内存优化 概述: 我们常常在开发场景下提及的内存是指程序内存. 程序内存可以分为以下五种: 1、 栈区(stack):栈的空间是连续的, 先进后出能保证不会产生内存碎片, 由高地址向低...
  • Android内存优化之图片优化

    万次阅读 多人点赞 2019-04-08 11:52:23
    相信大概刚开始学习Android的时候有过图片过大而直接报错的情况,下面简单介绍一下OOM问题,Android支持的图片格式及图片优化的几种方式 什么是OOM?:Android系统的进程(APP级别)有最大的内存限制,超过这个限制...
  • ESP32 优化笔记(一)内存优化

    千次阅读 2020-05-31 12:33:31
    内存优化内存优化 内存优化
  • react native内存优化

    千次阅读 2017-02-14 16:29:05
    react native在加载大量图片时小伙伴们会发现,内存一路飙升150M-200M那都不是问题,今天就来优化一下内存,降至40M左右。 1:实现一个module  import android.app.ActivityManager; import android.content....
  • Android内存优化

    千次阅读 2018-05-23 14:42:38
    参考Android性能优化(四)之内存优化实战 内存优化的套路: 解决所有的内存泄漏 集成LeakCanary,可以方便的定位出90%的内存泄漏问题; 通过反复进出可疑界面,观察内存增减的情况,Dump Java Heap获取当前堆栈...
  • 本文全面讲解性能优化中的所有知识,献上一份 Android性能优化的详细攻略, 含:优化方向、原因 &amp; 具体优化方案,希望你们会喜欢 文章较长,建议预留较长时间阅读 / 收藏 目录 1. 性能优化...
  • 内存作为计算机程序运行最重要的资源之一,需要运行过程中做到合理的资源分配与回收,不合理的内存占用轻则使得用户应用程序运行卡顿、ANR、黑屏,重则导致用户应用程序发生 OOM(out of...
  • 一.修改配置文件 找到IDEA安装的bin目录,找到idea64....-Xms 是最小启动内存参数 -Xmx 是最大运行内存参数 -XX:ReservedCodeCacheSize 保留代码占用的内存容量参数 8G内存的配置建议 -Xms512m -Xmx1024m -XX:R...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 1,086,336
精华内容 434,534
热门标签
关键字:

优化一下内存