精华内容
下载资源
问答
  • 关于进程间函数调用,以及回函数、共享内存的使用 最近在做项目时,需要调用一个第三方的库和框架,在项目进行一段时间之后,突然发现对该第三方的库以及框架的实现机制不太了解,现根据其函数声明以及使用方式...

    回调函数、共享内存概念与使用,以及进程间函数调用的讨论

        最近在做项目时,需要调用一个第三方的库和框架,在项目进行一段时间之后,突然发现对该第三方的库以及框架的实现机制不太了解,现根据其函数声明以及使用方式推导其实现机制。会有出入。

    1、问题描述

        该库和框架是用于仿真的,为非标库;同时提供了一个框架用于实现对库的使用,这样用户只需要在框架内写一些代码就可以实现相关的功能,这个软件的基本功能基本描述如下:

          1、一台“服务器”,同时有多个“客户端”;之所以加引号是因为不是严格意义上的服务器-客户端的概念,只是逻辑上功能一致;

          2、每个客户端采用相同的框架,并调用相同的库;

          3、为了保证运行时客户端之间数据传递的同步性,由服务器向每个客户端发送同样的数据,该数据包含了一些参数,通过这些参数,客户端之间保持同步运行并实现各自的操作;

        通过以上描述,最先想起来的是利用Socket,通过Socket(UDP)连接,每个客户端连接到服务器,再由服务器向每个客户端发送数据,也可以实现以上的功能;暂不讨论采用Socket方法的优劣。在这里采用另外一种实现方式:回调函数。回调函数的概念网上有一大堆,关于它的理解,在下面的章节我选取了一些比较好理解的进行展示(如有侵权,请及时联系)。

        结合该问题,通过具体实施,有助于加强对回调函数等的了解。

     

    2、回调函数

    2.1、回调函数的概念    

    关于回调函数的概念,网上有很多解释,但大多都很抽象。其实回调函数的概念也很简单,我的理解是:

          1、回调函数,Call Back Function,意如其名。可以简单的理解为A调用了B中的函数,而在B中的这个函数中又调用了A中的函数,这样就形成了一个逻辑上的“回调”机制;

          2、关于A和B的理解,可以理解为一个程序的两个部分;也可以理解为一个程序、一个库(静态or动态);当然最有助于理解的还是把A和B理解成两个程序,但是这样只是有助于理解,在实际实现过程中会存在很多的麻烦,接下来的章节会进行阐述;

          3、可以把回调函数理解成两层意思:一是回调函数的这个过程,是动态的;二是回调的那个函数,是静态的;

    2.2、回调函数的定义

        下面通过一个简单的例子来展示回调函数的使用:

     

    #include <stdio.h>
    void PrintWelcome(int n)    //被回调的函数
    {
        printf("%d is coming!", n);
    }
    
    void CallBack(int nC, void(*print)(int))    //调用“被回调的函数”的函数,简称回调函数
    {
        int nT = nC;
        print(nT);
    }
    
    void main()
    {
        CallBack(3, PrintWelcome);     //调用回调函数,其中一个参数为“被调用的函数”的函数名
        return;
    }

        由上面的代码可以看出:

        1)、main函数调用了CallBack函数;

        2)、由于main函数在调用CallBack时传递了PrintWelcome函数的函数指针,所以CallBack函数内部的print函数实际上就是在调用PrintWelcome函数;

        关于回调函数的使用,需要注意一下几点:

        1)、被回调的函数的定义方式:该函数的定义没有什么特别需要注意的地方,按照正常方式定义函数即可;另外需要注意的是,任何一个定义过的函数在内存中都会分配一块内存用于存储该函数的代码,存储形式为汇编语言的形式,而这块内存的首地址即为该函数名,即,函数名代表了该函数存储的内存地址的首地址

        2)、回调函数的定义:在定义回调函数时,必须保证该函数的一个参数为函数指针的形式,并且该函数指针代表的函数形式需和“被回调的函数”的函数形式保持一致!即,函数的返回值类型保持一致,函数参数列表保持一致

            为了解决函数形式很复杂的情况下,代码书写的问题,可以采用typedef将函数形式重定义成一个容易记的变量名,参考如下形式:

     

    typedef void (*PRINT)(int nC);

           当采用上述方式表达之后,回调函数就可以直接写成:

     

     

    void CallBack(int nC, PRINT print)
    {
        int nT = nC;
        print(nT);
    }

          可以明显看出这样写代码会更加简洁,并且不容易写错。

     

        3)、在调用回调函数时,需要保证传递参数必须为“被回调函数”的函数名!

    2.3、使用回调函数

        好了,回调函数的概念以及怎么使用搞清楚了,下面开始具体使用了,看第一章节的问题描述,开始使用回调函数设计程序。

        服务器是一个独立的程序,各个客户端分别是一个独立的程序,现在服务器要回调各个客户端程序中的函数。emmmmm.....问题来了,

        1)、两个独立的程序、不同的进程,服务器该怎么知道客户端中定义的函数呢?

          可以采用同一个函数定义的形式,双方都采用统一各头文件或者引用同一个库,这样大家都能识别同一种形式的函数定义了;

        2)、服务器端要调用回调函数,就需要知道“被回调函数”的函数名,那服务端怎么知道客户端中定义的“被回调的函数”的函数名呢?

          需要知道的是,这个“函数名”是有具体含义的,它不仅仅代表了函数的名称,也代表了函数定义存储在内存块上的首地址,而且在调用回调函数时,传递的参数实际上是地址

        3)、哦~那看来需要传递一个地址,那么我该怎么在程序间传递地址呢?

          Socket不行,它只能传递字符串;

          利用配置文件?不行,这样虽然能获取一个地址,但没有任何意义;

          诶,可以这样,我可以制作一个库,在这个库里,提供两个函数,一个用于得到“被回调函数”的地址,即set;一个用于获得“被回调函数”的地址,即get。然后服务器和客户端同时调用这一个库,一个set,一个get这样不接可以传递了吗!

        4)、需要注意的是,虽然两个程序都同时调用了同一个库,但是,这两个程序是分别把库加载进了进程申请的空间中的,虽然调用了同一个库,但是,这两个库之间是八竿子打不着的关系,更别提,数据传递了,这种方法不行,那有没有一种大家都认识的内存地址控件呢?

         还真有,那就是采用共享内存的方式。共享内存实际上是一个内核对象,内核对象大家都能识别,这样应该就能传递了吧。

     

    3、共享内存

    3.1、共享内存的概念

        共享内存又称为内存映射文件。共享内存从字面意义解释就是多个进程可以把一段内存映射到自己的进程空间,以此来实现数据的共享以及传输。意思就是,在计算机上申请了一块内存,该内存对所有的进程都可见,并且所有的进程都可以去访问它。需要注意的是,各进程是把这块内存地址映射到了自己的进程空间里,因为每一个打开的进程都有自己的进程空间,并不是在进程里都去操作那块地址,在进程里操作的是它的映射地址。

        拿Windows来说,Windows提供了多种机制来实现进程间的通信,比如最常见的Socket,还有RPC、COM、消息机制、邮件槽、管道等机制;但是,这些机制归根结底都会用到内存映射文件,即共享内存。

    3.2、共享内存的使用

        要使用共享内存,需要用到几个内核函数:

         1)、CreateFileMapping:告诉系统映射对象需要多大的物理存储空间,声明如下:

     

    HANDLE CreateFileMapping(
        HANDLE hFile,             //hFile是需要映射到进程地址空间的文件的句柄。如果没有有效的句柄,可以用INVALID_HANDLE_VALUE
        PSECURITY_ATTRIBUTES psa, //psa用于文件映射内核对象,一般传NULL(提供了默认的安全性,此外表明返回的句柄不可继承)
        DWORD fdwProtect,         //指定给物理存储器页面的保护属性,一般传PAGE_READWRITE即可,表示可读取数据并能够写入数据
        DWORD dwMaximumSizeHigh,  //用于告诉系统内存映射文件的最大大小,以字节为单位,该参数表示的是高32位,对于<4GB的数据来说,该参数始终为0
        DWORD dwMaximumSizeLow,   //该参数表示的是低32位,如果需要用当前的文件大小创建一个文件映射对象,两个参数都为0即可。
        PCTSTR pszName            //表示一个以'\0'结束的字符串,用来给文件映射对象指定一个名称。可以传NULL,也可以赋一个具有标识的字符串
    );

     

         2)、MapViewOfFile:将数据映射到进程的地址空间,声明如下:

     

    PVOID MapViewOfFile(
        HANDLE hFileMappingObject,  //文件映射对象的句柄,是之前调用CreateFileMapping函数时返回的
        DWORD dwDesireAccess,       //dwDesireAccess表示想要如何访问数据,可以指定为FILE_MAP_ALL_ACCESS(READ/WRITE/COPY)
        DWORD dwFileOffsetHigh,     //用于告诉系统应该把数据文件中的哪个字节映射到视图中的第一个字节。文件的偏移量必须是系统分配粒度的整数倍
        DWORD dwFileOffsetLow,      //这两个参数都可以指定为0
        SIZE_T dwNumberOfBytesToMap //用来指定需要映射到地址空间的数据的大小
    );

        下面通过两个例子用于展示共享内存的使用;

     

        ProcessA:

     

    #define DATA_SIZE  256
    const wchar_t mapFileName[] = L"myFirstMapping";
    HANDLE myFileMappingHandle = CreateFileMapping(INVALID_HANDLE_VALUE,NULL,PAGE_READWRITE,0,DATA_SIZE,mapFileName);
    if(myFileMappingHandle==NULL)
    {
        printf("创建文件映射内核对象失败!");
        CloseHandle(myFileMappingHandle);
        return 0;
    }
    char* pBuf = (char *)MapViewOfView(myFileMappingHandle,FILE_MAP_ALL_ACCESS,0,0,DATA_SIZE);
    while(1)
    {
        cout << "input..."  << endl;
        getchar();
        char szInfo[DATA_SIZE] = {0};
        gets_s(szInfo);
        strncpy(pBuf, szInfo, DATA_SIZE-1);
        pBuf[DATA_SIZE-1] = '\0';
    }
    UnmapViewOfFile(pICBR);
    CloseHandle(myFileMappingHandle);
    return 0;
    

        ProcessB:

     

    #define DATA_SIZE  256
    const wchar_t mapFileName[] = L"myFirstMapping";
    
    HANDLE myFileMappingHandle = CreateFileMapping(INVALID_HANDLE_VALUE,NULL,PAGE_READWRITE,0,DATA_SIZE,mapFileName);
    if(myFileMappingHandle==NULL)
    {
        printf("创建文件映射内核对象失败!");
        CloseHandle(myFileMappingHandle);
        return 0;
    }
    char* pBuf = (char *)MapViewOfView(myFileMappingHandle,FILE_MAP_ALL_ACCESS,0,0,DATA_SIZE);
    while(1)
    {
        cout << "output..."  << endl;
        getchar();
        cout << pBuf << endl;
    }
    UnmapViewOfFile(pICBR);
    CloseHandle(myFileMappingHandle);
    return 0;
     

     

     

     

     

     

    3.3、使用共享内存

        嗯,共享内存已经理解了,下面开始使用共享内存解决前面提出的问题。

        我在ProcessA里面定义了一个函数ICBR,调用set函数得到函数指针,并通过get函数获得函数指针并赋给内存变量(char* pBuf类型的变量),但是当我在ProcessB中获取内存变量的值时发现,获得地址并不正确。而且始终是同一个值。这是为什么呢?

        因为虽然两个进程都使用了共享内存,但是就向前面所说的一样,每一个进程在启动时,系统都会给进程分配一块虚拟的内存空间,不同进程的进程空间是独立的,而共享内存的使用仅仅是映射到进程空间,即在进程中得到的地址和实际上共享内存中存贮的地址是不一致的,它反映的仅仅是该进程空间中的内存地址,还是虚拟的。

        那还有什么方式能够实现吗?

    4、其实现的可能方式

        通过以上的分析,猜测其实现的可能方式如下:

         由上图所示,可能采用的方式是:

         1)、服务器端与客户端都加载了相同的库,在库中定义了Socket连接,以及回调函数的定义;

         2)、服务器端通过库发送了一系列数据:包括消息类型以及参数列表;消息类型用于表明该消息适用于回调函数的,参数列表用于表明向回调函数传递的参数;

         3)、当客户端的库收到了Socket消息之后,就调用回调函数函数,同时传参;

         4)、客户端的“被回调的函数”执行相关操作。

        之所以采用这种方式,而不采用直接用Socket的连接的方式,是因为,它需要把一部分操作给封装起来,同时,还需要向用户暴露一部分的操作,这样用户就能根据指定的框架和指定的参数来执行一些定制化的操作。

    5、进程间回调函数的调用

        在了解进程间回调函数的使用时,除了可以采用上述的间接的方式实现回调函数的间接调用外,好像还可以用COM接口实现同样的功能,未验证,不多做阐述。

    展开全文
  • 本文主要介绍了梅林entware环境与软件中心“虚拟内存”的冲突,这会致使entware无法正常安装或路由器重启后环境丢失,并给出解决方法。

    entware无法正常安装或路由器重启后环境丢失——梅林entware环境与软件中心“虚拟内存”的冲突

    由于梅林是基于Entware来安装插件的,因此需要安装entware环境。
    一般而言entware环境需要安装在U盘/移动硬盘中(推荐EXT4文件系统,可以用“分区助手”格式化方式更改挂载盘文件系统类型)

    # 检查
    df -h
    
    Filesystem                Size      Used Available Use% Mounted on
    /dev/sda1               916.9G      2.0G    868.4G   0% /tmp/mnt/sda1
    
    #安装entware环境
    entware-setup.sh
    

    在这里插入图片描述
    按照官方的流程,在选择安装到U盘后,等待安装即可。安装完成后会在U盘创建entware相关的文件,你就可以安装使用entware插件了。但是在安装过程中经常会出现“cannot create directory ‘XXX’:File exists”这类报错,以至于无法正常安装entware环境。
    在这里插入图片描述
    或者在安装完环境,路由器重启后entware环境丢失。

    基于上述问题,并经过排查,我发现在固件中的“软件中心”安装了“虚拟内存”后,上述问题将无法避免。

    在这里插入图片描述
    这是由于在“软件中心”安装“虚拟内存”后会导致jffs里的post-mount.sh在安装时不能正确产生,并且“虚拟内存”应用貌似卸载脚本有问题,正常卸载后相关的脚本仍然在jffs,这会致使entware环境重启后opt路径无效。同时本来安装 entware-ng就会创建虚拟内存。

    因此:

    情况一:先安装了“软件中心-虚拟内存”,后安装entware环境,entware将无法正常安装
    情况二:先安装entware环境,后安装了“软件中心-虚拟内存”,路由器重启后entware环境丢失

    解决方法:

    还原路由器至出厂值,进入 “系统管理–系统设置”,按图示开启JFFS,重启路由器以初始化JFFS。

    在这里插入图片描述

    注意:重启后检查Format JFFS partition at next boot是否已经自动还原为”否“,如果没有,手动调成“否”,否则下次开机会再次还原配置。

     Info:  /tmp/mnt/sda1 selected.
    
     Info:  Creating /tmp/mnt/sda1/entware folder...
     * Warning:  Deleting old /tmp/opt symlink...
     Info:  Creating /tmp/opt symlink...
     Info:  Creating /jffs scripts backup...
     tar: removing leading \\'/\\' from member names
     Info:  Modifying start scripts...
     Info:  Starting Entware deployment....
    
    Info: Creating folders...
    Info: Deploying opkg package manager...
    Downloading /opt/bin/opkg... success!
    Downloading /opt/etc/opkg.conf... success!
    Downloading /opt/etc/profile... success!
    Downloading /opt/etc/init.d/rc.func... success!
    Downloading /opt/etc/init.d/rc.unslung... success!
    Info: Basic packages installation...
    Downloading http://pkg.entware.net/binaries/mipsel/Packages.gz.
    Updated list of available packages in /opt/var/opkg-lists/entware-ng.
    Installing ldconfig (1.0.12-1) to root...
    Downloading http://pkg.entware.net/binaries/mipsel/ldconfig_1.0.12-1_mipselsf.ipk.
    Installing findutils (4.5.14-1) to root...
    Downloading http://pkg.entware.net/binaries/mipsel/findutils_4.5.14-1_mipselsf.ipk.
    Installing libc (1.0.12-1) to root...
    Downloading http://pkg.entware.net/binaries/mipsel/libc_1.0.12-1_mipselsf.ipk.
    Installing libgcc (4.8.5-1) to root...
    Downloading http://pkg.entware.net/binaries/mipsel/libgcc_4.8.5-1_mipselsf.ipk.
    Installing libssp (4.8.5-1) to root...
    Downloading http://pkg.entware.net/binaries/mipsel/libssp_4.8.5-1_mipselsf.ipk.
    Configuring ldconfig.
    Configuring libgcc.
    Configuring libc.
    Configuring libssp.
    Configuring findutils.
     
    Congratulations! If there are no errors above then Entware-ng is successfully initialized.
     
    Found a Bug? Please report at https://github.com/Entware-ng/Entware-ng/issues
     
    Type \\'opkg install \\' to install necessary package.
    
    展开全文
  • 因为我们对处理器个进行了改动,导致系统无法启动,所以我们只需要把我们改动的内存设置进行删除就可以解决问题,网上找到的其他方法别乱用,本人的同事因为那些方法删除了几款新安装的软件,所幸没有重要数据awa ...

    废话不多说,本人自百度知道网站转载

    看后别忘了给 lenxuekuangmo 大佬点个赞

    https://zhidao.baidu.com/question/1819534353408687948.html?sort=11&rn=5&pn=0#wgt-answers.

    网上查到的最好用的方法,因为我们对处理器个数进行了改动,导致系统无法启动,所以我们只需要把我们改动的内存设置进行删除就可以解决问题,网上找到的其他方法别乱用,本人的同事因为那些方法删除了几款新安装的软件,所幸没有重要数据(游戏)丢失awa,不然后果不堪设想…

    展开全文
  • App内存占用优化

    千次阅读 2017-01-12 18:47:04
    RAM(Random-access memory)在任何软件开发中都是非常宝贵的资源,移动操作系统由于其物理内存的局限性更是如此。尽管ART(Android Runtime)与Dalvik虚拟机会执行常规的垃圾回收,但这并不意味着可以忽略App中的...

    RAM(Random-access memory)在任何软件开发中都是非常宝贵的资源,移动操作系统由于其物理内存的局限性更是如此。尽管ART(Android Runtime)与Dalvik虚拟机会执行常规的垃圾回收,但这并不意味着可以忽略App中的内存分配与释放。我们应当避免引起内存泄露,如持有静态成员变量而导致无法释放,应当在应用的生命周期回调中释放掉所有的引用。

    本文主要介绍如何减少App中的内存使用。

    监控可用内存及内存使用状况

    Android 框架与Android Studio可以帮助我们来分析和调整App的内存使用,其中Android框架提供了一些API来帮助App在运行时动态减少内存占用,Android Studio包括一些工具来查看内存的使用情况。

    RAM使用分析工具

    在优化内存问题之前,需要先找到这些问题,Android Studio及Android SDK提供了几个工具用来分析App中的内存使用:

    1. Android Studio中的Memory Monitor

      该工具可以显示一个会话过程中的内存分配情况,有一个可视化的图形界面,可以看到Java内存随时间的变化情况以及GC事件。当App运行时,可以启动GC操作并且获取Java Heap的快照。该工具的输出可以帮助我们定位哪里容易导致频繁的垃圾回收,从而导致应用程序变慢。

    2. Android Studio中的Allocation Tracker工具

      该工具记录了一个App的内存分配情况并在分析快照中列出了所有分配的对象。可以使用此工具找到分配过多对象的部分代码。

    响应回调释放内存

    不同的Android设备或不同的用户操作会导致不同的内存占用状况,Android系统在遇到内存压力的情况下会发出信号预警,App需要监听这些信号来调整内存的使用。

    可以使用ComponentCallbacks2 API来监听回调以调整内存使用状态。onTrimMemory()可以允许App监听内存相关的事件,无论App是在前台运行还是在后台运行。下面是一个示例,通过实现Activity的onTrimMemory()方法来监听内存相关的回调。

    import android.content.ComponentCallbacks2;
    // Other import statements ...
    
    public class MainActivity extends AppCompatActivity
        implements ComponentCallbacks2 {
    
        // Other activity code ...
    
        /**
         * Release memory when the UI becomes hidden or when system resources become low.
         * @param level the memory-related event that was raised.
         */
        public void onTrimMemory(int level) {
    
            // Determine which lifecycle or system event was raised.
            switch (level) {
    
                case ComponentCallbacks2.TRIM_MEMORY_UI_HIDDEN:
    
                    /*
                       Release any UI objects that currently hold memory.
    
                       The user interface has moved to the background.
                    */
    
                    break;
    
                case ComponentCallbacks2.TRIM_MEMORY_RUNNING_MODERATE:
                case ComponentCallbacks2.TRIM_MEMORY_RUNNING_LOW:
                case ComponentCallbacks2.TRIM_MEMORY_RUNNING_CRITICAL:
    
                    /*
                       Release any memory that your app doesn't need to run.
    
                       The device is running low on memory while the app is running.
                       The event raised indicates the severity of the memory-related event.
                       If the event is TRIM_MEMORY_RUNNING_CRITICAL, then the system will
                       begin killing background processes.
                    */
    
                    break;
    
                case ComponentCallbacks2.TRIM_MEMORY_BACKGROUND:
                case ComponentCallbacks2.TRIM_MEMORY_MODERATE:
                case ComponentCallbacks2.TRIM_MEMORY_COMPLETE:
    
                    /*
                       Release as much memory as the process can.
    
                       The app is on the LRU list and the system is running low on memory.
                       The event raised indicates where the app sits within the LRU list.
                       If the event is TRIM_MEMORY_COMPLETE, the process will be one of
                       the first to be terminated.
                    */
    
                    break;
    
                default:
                    /*
                      Release any non-critical data structures.
    
                      The app received an unrecognized memory level value
                      from the system. Treat this as a generic low-memory message.
                    */
                    break;
            }
        }
    }

    onTrimMemory()回调是在Android4.0(API Level 14)添加的,对于之前的版本,可以使用onLowMemory()回调替代,它大致相当于TRIM_MEMORY_COMPLETE事件。

    检测应该使用多少内存

    Android为了支持多进程,因此为每个App占用的内存做了限制。由于不同设备的RAM大小不同,因此分配给每个App的Heap大小也会有差异。当App已经到达了特定的Heap限制,如果再进行内存分配的话,就会抛出 OutOfMemoryError异常。

    为了避免内存溢出,我们可以通过getMemoryInfo()查询当前设备上还有多少可用的内存空间,该方法返回一个ActivityManager.MemoryInfo对象,它包含了设备当前的内存状态,如可用内存、总内存以及内存阈值(当可用内存低于该阈值时,系统就会杀死部分进程)等信息。ActivityManager.MemoryInfo有一个叫做lowMemory的布尔属性,表示设备是否处于低内存状态。

    下面是一个使用getMemoryInfo()的示例:

    public void doSomethingMemoryIntensive() {
    
        // Before doing something that requires a lot of memory,
        // check to see whether the device is in a low memory state.
        ActivityManager.MemoryInfo memoryInfo = getAvailableMemory();
    
        if (!memoryInfo.lowMemory) {
            // Do memory intensive work ...
        }
    }
    
    // Get a MemoryInfo object for the device's current memory status.
    private ActivityManager.MemoryInfo getAvailableMemory() {
        ActivityManager activityManager = (ActivityManager) this.getSystemService(ACTIVITY_SERVICE);
        ActivityManager.MemoryInfo memoryInfo = new ActivityManager.MemoryInfo();
        activityManager.getMemoryInfo(memoryInfo);
        return memoryInfo;
    }

    使用高效、内存占用少的代码结构

    一些Android特性、Java类、代码结构会使用更多的内存,可通过使用更高效的代码结构来节省内存。

    有节制地使用Service

    让一个不再需要的Service保持运行是Android开发中最糟糕的内存管理错误之一。如果App需要Service来执行后台任务,需要在它完成任务时终止它,否则可能导致内存泄露。

    当启动一个Service时,系统会保持该服务所在的进程,这样该服务占用的内存将不能被其他进程使用。同时系统通过LRU Cache缓存的的进程数量也将减少,从而降低进程间切换的效率。当内存紧张或系统无法为当前所运行的Service提供足够的进程时还会发生系统抖动。

    应当避免使用持久运行的Service,因为它们对内存有持续的需求,建议使用JobScheduler

    如果必须使用Service,可以使用IntentService来限制其生命周期,IntentService会在处理完任务之后终止。

    使用优化的数据容器

    一些编程语言提供的类可能并未针对移动设备做优化,例如通用的HashMap实现是比较低效的,因为每一个映射都需要一个单独的Entry对象。

    Android框架提供了一些优化过的数据容器,如SparseArraySparseBooleanArrayLongSparseArray。例如SparseArray更加高效,是因为它避免了对key及一些value的自动装箱操作。

    谨慎使用代码抽象

    开发者经使用抽象来简化编程,因为抽象可以提高代码的灵活性,也更方便维护。但是抽象会带来明显的内存消耗:抽象一般来说需要执行更多的代码、需要更多的时间以及RAM空间来将代码映射到内存。所以如果抽象不能带来明显的好处,应当避免使用代码抽象。

    例如,枚举占用的内存通常是静态常量的两倍,需要严格避免在Android中使用枚举。

    使用Nano版本protobufs序列化数据

    Protocol buffers是Google出品的独立于平台及语言的、可拓展的结构化数据序列化技术,它类似于XML,但更加轻量级、快速、简洁。如果决定使用protobufs来序列化数据,应该在客户端选择使用Nano版本,因为常规版本的protobufs会生成极其冗长的代码,从而导致App端出现各种问题,如内存溢出、APK大小增加、执行速度变慢等。

    Nano版本Protobufs的相关参考:protobuf readme

    避免内存泄露

    垃圾回收通常不会影响应用的性能,但是短时间内的垃圾收集将会占用帧时间,垃圾回收占用的时间越多,用到其他事情上的时间就越少。

    通常,内存泄露会导致频繁地垃圾回收事件的发生,在实践中,内存泄露描述了给定时间内分配的临时对象的数量。

    例如,可能在for循环中分配多个临时对象,或者在View的onDraw()方法中创建多个Paint、Bitmap对象。上述情况下,App会快速创建大量对象,从而迅速消耗掉新生代中的内存,导致GC的发生。

    我们需要找到内存泄露的地方并进行修复,如将实例化操作移出for循环,不要在onDraw()这种频繁调用的方法中创建对象。

    移除内存密集型的资源和库

    代码中的一些资源及Library可能会在我们不知情的情况下吞噬内存。一个APK中,第三方库或者嵌入的资源会影响到App占用的内存总量。可以通过移除冗余的资源、臃肿的组件及不必要的Library来优化内存消耗。

    减小APK大小

    通过减小APK的大小可以明显减低App对内存的占用。Bitmap大小、资源、动画帧图像以及第三方库影响APK的大小,Android Studio及Android SDK提供了一些工具用来减少资源大小及外部依赖。

    更多关于APK的瘦身方案,可参考Reduce APK Size

    使用Dagger2实现依赖注入

    依赖注入框架可以简化代码并为测试及其他配置变化提供适配环境。

    如果打算在App中使用依赖注入框架的话,建议使用Dagger2。Dagger没有使用反射,它的静态、编译时实现意味着它不需要运行时成本及内存消耗。

    其他使用反射的依赖注入框架需要扫描代码来寻找注解,这个过程可能需要更多的CPU周期和RAM,并可能导致应用程序启动的明显滞后。

    谨慎使用外部Library

    第三方的Library代码可能并非为移动环境所写,当应用到移动客户端时可能导致性能降低。当决定使用一个第三方库时,需要针对移动环境做优化,另外需要分析Library的代码大小及内存占用情况,最后再决定是否应用该Library。

    哪怕是一些针对移动环境做过优化的Library因为不同的实现也可能导致一些问题,如一种情况使用了Nano版的protobufs,另一种情况使用了Micro版的protobufs,不同的Library实现有可能导致意想不到的问题。

    尽管Proguard可以移除无效的API及资源,但是它无法移除一个Library大的内部依赖。这些库中的功能可能需要较低的依赖项,例如当使用一个库提供的Activity子类时,可能会引入大量的依赖。

    需要避免只使用一个库中的有限的功能而引入库的情况,我们不希望引入大量不需要的代码。当决定是否使用一个Library时,尽可能高度匹配我们的需求,否则,可以考虑自己实现。

    参考文献Manage Your App’s Memory

    展开全文
  • 内存泄漏和内存溢出

    万次阅读 多人点赞 2018-08-22 15:28:58
    内存溢出:(out of memory)通俗理解就是内存不够,通常在运行大型软件或游戏时,软件或游戏所需要的内存远远超出了你主机内安装的内存所承受大小,就叫内存溢出。 内存泄漏:(Memory Leak)是指程序中己动态分配...
  • 不得说不说, 这个软件太吃内存,原本4g的内存条根本扛不住,然后在某平台买了个8g的内存条加上,凑够了12g,但在浏览器打开几个网站,同时android studio 开了两三个项目,这时候使用真机,好几次就360的小滚球...
  • Ryzen平台下内存超频与内存时序

    万次阅读 2018-10-24 10:41:40
    最近在AMD平台超内存,顺便学习下内存知识,以下内容转自: 内存的工作原理及时序...内存超频软件推荐: Thaiphoon Burner (内存查看器) DRAM Calculator for Ryzen 1.3.1 (AMD平台内存超频计算器) MemTe...
  • Android内存管理机制详解

    万次阅读 多人点赞 2012-12-13 10:37:33
    与windows内存区别 在Linux中经常发现空闲内存很少,似乎所有的内存都被系统占用了,表面感觉是内存不够用了,其实不然。这是Linux内存管理的一个优秀特性,在这方面,区别于 Windows的内存管理。主要特点是,无论...
  • Java之:JVM内存模型

    千次阅读 2016-06-17 15:49:35
    所有的线程共享JVM内存区域main memory,而每个线程又单独的有自己的工作内存,当线程与内存区域进行交互时,数据从主存拷贝到工作内存,进而交由线程处理(操作码+操作)。在之前,我们也已经提到,JVM的逻辑内存...
  • 起因 在使用单位x270(i5 7200U)时明显感觉...查看拆机图片后发现X270仅有一个内存槽,主板支持DDR4 2133Mhz内存。在二手东和淘宝之间挑选了二手东十铨(Team) DDR4 2400 8GB 笔记本内存。 主板预留M2 2242接...
  • lab2 系统内存的探测 参考博客 主要涉及操作系统的物理内存管理。 操作系统为了使用内存,还需高效地管理内存资源。 这里我们会了解并且自己动手完成一个简单的物理内存管理系统。 实验目的 ...
  • cache、内存、虚拟内存

    千次阅读 2017-09-30 21:15:08
    cache、内存、虚拟内存 内存就是RAM,RAM和ROM是相对的,RAM在断掉后保存在里面的信息会消失,而ROM在断电后存储在里面的信息不会消失。RAM分为静态RAM和动态RAM,静态RAM叫SRAM,是仅次于CPU访问速度的RAM,L1 ...
  • 内存监视

    千次阅读 2017-05-28 11:29:49
    熟悉Windows存储器管理中提供的各种机制和实现的请求页和群集技术。 Windows提供给应用程序的内存方式具有统一的简明和保护性的特点。另外,用户不需要知道操作系统如何分配内存,只需要知道应用程序如何分配内存...
  • Android最佳性能实践(一)——合理管理内存

    万次阅读 多人点赞 2015-02-05 09:20:28
    有不少朋友都问过我,怎样才能写出高性能的应用程序,如何避免程序出现OOM,...内存(RAM)对于任何一个软件开发环境都是种非常珍贵的资源,而对于移动操作系统来讲的话,则会显得更加珍贵,因为手机的硬件条件相对于PC
  • Android内存管理优化建议

    千次阅读 2016-10-17 12:42:35
    前面我们提到过使用getMemoryClass()的方法可以...简要的获取某个应用的内存占用情况可以参考下面的示例( 关于更多内存查看的知识,可以参考这篇官方教程:Investigating Your RAM Usage )1)查看内存使用情况 通过
  • 这是诡计 我实在不愿意提起这个话题,因此我的软件在此之前也厚颜无耻地用到了这种诡计。后来在网上看到几篇文章,深深感觉到,已经有程序员站出来,揭穿这个忽悠了千百万用户的诡计了;此刻,需要更多的程序员站...
  • Oracle-内存管理解读

    千次阅读 2016-11-11 00:47:27
    概述关于内存的配置,是最影响 Oracle性能的配置。内存还直接影响到其他两个重要资源的消耗: CPU 和 IO.那Oracle 内存存储的主要内容是什么呢? 程序代码( PLSQL、 Java); 关于已经连接的会话的信息,包括当前...
  • 内存泄漏memory leak和内存溢出OOM

    千次阅读 2017-10-02 14:50:36
    内存泄漏为什么会产生内存泄漏?当一个对象已经不需要再使用本该被回收时,另外一个正在使用的对象持有它的引用从而导致它不能被回收,这导致本该被回收的对象不能被回收而停留在堆内存中,这就产生了内存泄漏。简单...
  • 内存泄漏和内存溢出的联系与区别

    万次阅读 多人点赞 2018-04-28 11:43:37
    一:内存泄漏(memory leak)1:内存泄漏是指程序中已动态分配的堆内存由于某种原因未释放或无法释放,造成系统内存的浪费,导致程序运行速度减慢甚至系统奔溃等严重后果。2:一次内训泄漏似乎不会有大的影响,但...
  • 内存溢出与泄漏

    万次阅读 2017-09-02 22:53:25
    比如申请了一个integer,但给它存了long才能存下的,那就是内存溢出。内存泄露 memory leak,是指程序在申请内存后,无法释放已申请的内存空间,一次内存泄露危害可以忽略,但内存泄露堆积后果很严重,无论多少内存...
  • 从CSDN各个博客上摘选的一些容易做错的嵌入式软件的笔试题,做一下记录,让自己记住。 文章转自:嵌入式经典面试题。 1、用预处理指令#define 声明一个常数,用以表明1年中有多少秒(忽略闰年问题) 解答:这一...
  • 首先内存泄漏问题、内存溢出问题可都能会OOM(OutofMemoryError) 堆空间不足 1、内存泄漏问题导致 内存泄漏:是指在堆空间中一直有引用链引用着某些对象。导致对象不能被垃圾收集。 解决办法:如果是内存泄漏,课...
  • Android 开发 Eclipse 内存调整

    千次阅读 2015-02-04 14:10:11
    在使用Eclipse的过程中,有时会遇到使用Java虚拟机内存不够的情况,这时Eclipse就会提示你重启,我们可以通过设定Eclipse启动参数来调节使用Java虚拟机内存。 右键点击Eclipse的快捷方式,选择属性,在目标的Text里...
  • 转自:https://blog.csdn.net/rebirthme/article/details/50402082想必在linux上写过程序的同学都有分析进程占用多少内存的经历,或者被问到这样的问题——你的程序在运行时占用了多少内存(物理内存)?通常我们可以...
  • Linux用户态进程如何监控内存被写事件

    千次阅读 多人点赞 2019-06-01 08:23:33
    上周了解到一个很好玩的问题,即 如何捕获到“一块特定的内存的内容变成某一个特定的值”这么一个事件。 嗯,还是那位暴雨天穿着意尔康皮鞋给我们送伞皮鞋湿了的同事,感谢他能提供一些好玩的东西来折腾。 我并不...
  • winserver物理内存使用不到一半就频繁爆内存

    千次阅读 多人点赞 2020-08-21 01:59:08
    winserver该不该设置虚拟内存?使用的物理内存内存使用的区别?提交、专用、工作集的区别?已提交又是什么东西? 如何配好winserver内存线上一直存在一个问题,内存无法最大利用化,经常出现服务崩溃问题。 win...
  • 关于Android内存优化你应该知道的一切

    千次阅读 多人点赞 2016-08-04 22:00:23
    介绍在Android系统中,内存分配与释放分配在一定程度上会影响App性能的—鉴于其使用的是类似于Java的GC回收机制,因此系统会以消耗一定的效率为代价,进行垃圾回收。 在中国有句老话:”由俭入奢易,由奢返俭难”。...
  • 内存泄漏检测工具

    万次阅读 2011-11-02 14:11:51
    内存泄漏(memory leak),指由于疏忽或错误造成程序未能释放已经不再使用的内存的情况。在编程时进行动态内存分配是非常必要的,它可以在程序运行的过程中帮助分配所需的内存,而不是在进程启动的时候就进行分配。...
  • Android 内存管理机制

    千次阅读 2018-09-03 14:41:45
    本文主要包括三大部分内容: 内存管理基础:从整个计算机领域... Android的内存管理相关知识:Android又不同于Linux,它是一个移动操作系统,因此其内存管理上也有自己的特性,这一部分详细讲述Android的内存管理...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 132,804
精华内容 53,121
关键字:

怎么调软件内存数