Android系统匿名共享内存Ashmem(Anonymous Shared Memory)驱动程序源代码分析(下)

简介:

 四. 匿名共享内存的锁定和解锁操作
        前面提到,Android系统的运行时库提到了执行匿名共享内存的锁定和解锁操作的两个函数ashmem_pin_region和ashmem_unpin_region,它们实现在system/core/libcutils/ashmem-dev.c文件中:

 
 
  1. int ashmem_pin_region(int fd, size_t offset, size_t len)  
  2. {  
  3.     struct ashmem_pin pin = { offset, len };  
  4.     return ioctl(fd, ASHMEM_PIN, &pin);  
  5. }  
  6.  
  7. int ashmem_unpin_region(int fd, size_t offset, size_t len)  
  8. {  
  9.     struct ashmem_pin pin = { offset, len };  
  10.     return ioctl(fd, ASHMEM_UNPIN, &pin);  

       它们的实现很简单,通过ASHMEM_PIN和ASHMEM_UNPIN两个ioctl操作来实现匿名共享内存的锁定和解锁操作。

 

       我们先看来一下ASHMEM_PIN和ASHMEM_UNPIN这两个命令号的定义,它们的定义可以在kernel/common/include/linux/ashmem.h文件中找到:

 
 
  1. #define __ASHMEMIOC     0x77  
  2.  
  3. #define ASHMEM_PIN      _IOW(__ASHMEMIOC, 7, struct ashmem_pin)  
  4. #define ASHMEM_UNPIN        _IOW(__ASHMEMIOC, 8, struct ashmem_pin) 

       它们的参数类型为struct ashmem_pin,它也是定义在kernel/common/include/linux/ashmem.h文件中:

 
 
  1. struct ashmem_pin {  
  2.     __u32 offset;   /* offset into region, in bytes, page-aligned */  
  3.     __u32 len;  /* length forward from offset, in bytes, page-aligned */  
  4. }; 

       这个结构体只有两个域,分别表示要锁定或者要解锁的内块块的起始大小以及大小。

 

       在分析这两个操作之前,我们先来看一下Ashmem驱动程序中的一个数据结构struct ashmem_range,这个数据结构就是用来表示某一块被解锁(unpinnd)的内存:

 
 
  1. /*  
  2.  * ashmem_range - represents an interval of unpinned (evictable) pages  
  3.  * Lifecycle: From unpin to pin  
  4.  * Locking: Protected by `ashmem_mutex'  
  5.  */  
  6. struct ashmem_range {  
  7.     struct list_head lru;       /* entry in LRU list */  
  8.     struct list_head unpinned;  /* entry in its area's unpinned list */  
  9.     struct ashmem_area *asma;   /* associated area */  
  10.     size_t pgstart;         /* starting page, inclusive */  
  11.     size_t pgend;           /* ending page, inclusive */  
  12.     unsigned int purged;        /* ASHMEM_NOT or ASHMEM_WAS_PURGED */  
  13. }; 

        域asma表示这块被解锁的内存所属于的匿名共享内存,它通过域unpinned连接在asma->unpinned_list表示的列表中;域pgstart和paend表示这个内存块的开始和结束页面号,它们表示一个前后闭合的区间;域purged表示这个内存块占用的物理内存是否已经被回收;这块被解锁的内存块除了保存在它所属的匿名共享内存asma的解锁列表unpinned_list之外,还通过域lru保存在一个全局的最近最少使用列表ashmem_lru_list列表中,它的定义如下:
 

 
 
  1. /* LRU list of unpinned pages, protected by ashmem_mutex */  
  2. static LIST_HEAD(ashmem_lru_list); 

       了解了这个数据结构之后,我们就可以来看ashmem_ioctl函数中关于ASHMEM_PIN和ASHMEM_UNPIN的操作了:

 
 
  1. static long ashmem_ioctl(struct file *file, unsigned int cmd, unsigned long arg)  
  2. {  
  3.     struct ashmem_area *asma = file->private_data;  
  4.     long ret = -ENOTTY;  
  5.  
  6.     switch (cmd) {  
  7.     ......  
  8.     case ASHMEM_PIN:  
  9.     case ASHMEM_UNPIN:  
  10.         ret = ashmem_pin_unpin(asma, cmd, (void __user *) arg);  
  11.         break;  
  12.     ......  
  13.     }  
  14.  
  15.     return ret;  

        它们都是通过ashmem_pin_unpin来进一步处理:

 
 
  1. static int ashmem_pin_unpin(struct ashmem_area *asma, unsigned long cmd,  
  2.                 void __user *p)  
  3. {  
  4.     struct ashmem_pin pin;  
  5.     size_t pgstart, pgend;  
  6.     int ret = -EINVAL;  
  7.  
  8.     if (unlikely(!asma->file))  
  9.         return -EINVAL;  
  10.  
  11.     if (unlikely(copy_from_user(&pin, p, sizeof(pin))))  
  12.         return -EFAULT;  
  13.  
  14.     /* per custom, you can pass zero for len to mean "everything onward" */  
  15.     if (!pin.len)  
  16.         pin.len = PAGE_ALIGN(asma->size) - pin.offset;  
  17.  
  18.     if (unlikely((pin.offset | pin.len) & ~PAGE_MASK))  
  19.         return -EINVAL;  
  20.  
  21.     if (unlikely(((__u32) -1) - pin.offset < pin.len))  
  22.         return -EINVAL;  
  23.  
  24.     if (unlikely(PAGE_ALIGN(asma->size) < pin.offset + pin.len))  
  25.         return -EINVAL;  
  26.  
  27.     pgstart = pin.offset / PAGE_SIZE;  
  28.     pgend = pgstart + (pin.len / PAGE_SIZE) - 1;  
  29.  
  30.     mutex_lock(&ashmem_mutex);  
  31.  
  32.     switch (cmd) {  
  33.     case ASHMEM_PIN:  
  34.         ret = ashmem_pin(asma, pgstart, pgend);  
  35.         break;  
  36.     case ASHMEM_UNPIN:  
  37.         ret = ashmem_unpin(asma, pgstart, pgend);  
  38.         break;  
  39.     ......  
  40.     }  
  41.  
  42.     mutex_unlock(&ashmem_mutex);  
  43.  
  44.     return ret;  

       首先是获得用户空间传进来的参数,并保存在本地变量pin中,这是一个struct ashmem_pin类型的变量,这个结构体我们在前面已经见过了,它包括了要pin/unpin的内存块的起始地址和大小,这里的起始地址和大小都是以字节为单位的,因此,通过转换把它们换成以页面为单位的,并且保存在本地变量pgstart和pgend中。这里除了要对参数作一个安全性检查外,还要一个处理逻辑是,如果从用户空间传进来的内块块的大小值为0 ,则认为是要pin/unpin整个匿名共享内存。

 

        函数最后根据当前要执行的是ASHMEM_PIN操作还是ASHMEM_UNPIN操作来分别执行ashmem_pin和ashmem_unpin来进一步处理。创建匿名共享内存时,默认所有的内存都是pinned状态的,只有用户告诉Ashmem驱动程序要unpin某一块内存时,Ashmem驱动程序才会把这块内存unpin,之后,用户可以再告诉Ashmem驱动程序要重新pin某一块之前被unpin过的内块,从而把这块内存从unpinned状态改为pinned状态,也就是说,执行ASHMEM_PIN操作时,目标对象必须是一块当前处于unpinned状态的内存块。

       我们先来看一下ASHMEM_UNPIN操作,进入到ashmem_unpin函数:

 
 
  1. /*  
  2.  * ashmem_unpin - unpin the given range of pages. Returns zero on success.  
  3.  *  
  4.  * Caller must hold ashmem_mutex.  
  5.  */  
  6. static int ashmem_unpin(struct ashmem_area *asma, size_t pgstart, size_t pgend)  
  7. {  
  8.     struct ashmem_range *range, *next;  
  9.     unsigned int purged = ASHMEM_NOT_PURGED;  
  10.  
  11. restart:  
  12.     list_for_each_entry_safe(range, next, &asma->unpinned_list, unpinned) {  
  13.         /* short circuit: this is our insertion point */  
  14.         if (range_before_page(range, pgstart))  
  15.             break;  
  16.  
  17.         /*  
  18.          * The user can ask us to unpin pages that are already entirely  
  19.          * or partially pinned. We handle those two cases here.  
  20.          */  
  21.         if (page_range_subsumed_by_range(range, pgstart, pgend))  
  22.             return 0;  
  23.         if (page_range_in_range(range, pgstart, pgend)) {  
  24.             pgstart = min_t(size_t, range->pgstart, pgstart),  
  25.             pgend = max_t(size_t, range->pgend, pgend);  
  26.             purged |= range->purged;  
  27.             range_del(range);  
  28.             goto restart;  
  29.         }  
  30.     }  
  31.  
  32.     return range_alloc(asma, range, purged, pgstart, pgend);  

       这个函数的主体就是在遍历asma->unpinned_list列表,从中查找当前处于unpinned状态的内存块是否与将要unpin的内存块[pgstart, pgend]是否相交,如果相交,则要执行合并操作,即调整pgstart和pgend的大小,然后通过调用range_del函数删掉原来的已经被unpinned过的内存块,最后再通过range_alloc函数来重新unpinned这块调整过后的内存块[pgstart, pgend],这里新的内存块[pgstart, pgend]已经包含了刚才所有被删掉的unpinned状态的内存。注意,这里如果找到一块相并的内存块,并且调整了pgstart和pgend的大小之后,要重新再扫描一遍asma->unpinned_list列表,因为新的内存块[pgstart, pgend]可能还会与前后的处于unpinned状态的内存块发生相交。
 

 

        我们来看一下range_before_page的操作,这是一个宏定义:

 
 
  1. #define range_before_page(range, page) \  
  2.   ((range)->pgend < (page)) 

        表示range描述的内存块是否在page页面之前,如果是,则整个描述就结束了。从这里我们可以看出asma->unpinned_list列表是按照页面号从大到小进行排列的,并且每一块被unpin的内存都是不相交的。

 

        再来看一下page_range_subsumed_by_range的操作,这也是一个宏定义:

 
 
  1. #define page_range_subsumed_by_range(range, start, end) \  
  2.   (((range)->pgstart <= (start)) && ((range)->pgend >= (end))) 

       表示range描述的内存块是不是包含了[start, end]这个内存块,如果包含了,则说明当前要unpin的内存块已经处于unpinned状态,什么也不用操作,直接返回即可。

 

       再看page_range_in_range的操作,它也是一个宏定义:

 
 
  1. #define page_range_in_range(range, start, end) \  
  2.   (page_in_range(range, start) || page_in_range(range, end) || \  
  3.    page_range_subsumes_range(range, start, end)) 

      它用到的其它两个宏分别定义为:

 
 
  1. #define page_range_subsumed_by_range(range, start, end) \  
  2.   (((range)->pgstart <= (start)) && ((range)->pgend >= (end)))  
  3.  
  4. #define page_in_range(range, page) \  
  5.  (((range)->pgstart <= (page)) && ((range)->pgend >= (page))) 

      它们都是用来判断两个内存区间是否相交的。

 

      两个内存块相交分为四种情况:

      |-------range-----|        |-------range------|       |--------range---------|                 |----range---|

       |-start----end-|       |-start-----end-|                          |-start-------end-|        |-start-----------end-|
                 (1)                                (2)                                  (3)                                            (4)
      第一种情况,前面已经讨论过了,对于第二到第四种情况,都是需要执行合并操作的。

      再来看从asma->unpinned_list中删掉内存块的range_del函数:

 
 
  1. static void range_del(struct ashmem_range *range)  
  2. {  
  3.     list_del(&range->unpinned);  
  4.     if (range_on_lru(range))  
  5.         lru_del(range);  
  6.     kmem_cache_free(ashmem_range_cachep, range);  

      这个函数首先把range从相应的unpinned_list列表中删除,然后判断它是否在lru列表中:

 
 
  1. #define range_on_lru(range) \  
  2.   ((range)->purged == ASHMEM_NOT_PURGED) 

      如果它的状态purged等于ASHMEM_NOT_PURGED,即对应的物理页面尚未被回收,它就位于lru列表中,通过调用lru_del函数进行删除:
 

 
 
  1. static inline void lru_del(struct ashmem_range *range)  
  2. {  
  3.     list_del(&range->lru);  
  4.     lru_count -= range_size(range);  

       最后调用kmem_cache_free将它从slab缓冲区ashmem_range_cachep中释放。

 

       这里的slab缓冲区ashmem_range_cachep定义如下:

 
 
  1. static struct kmem_cache *ashmem_range_cachep __read_mostly; 

      它和前面介绍的slab缓冲区ashmem_area_cachep一样,是在Ashmem驱动程序模块初始化函数ashmem_init进行初始化的:

 
 
  1. static int __init ashmem_init(void)  
  2. {  
  3.     int ret;  
  4.  
  5.     ......  
  6.  
  7.     ashmem_range_cachep = kmem_cache_create("ashmem_range_cache",  
  8.         sizeof(struct ashmem_range),  
  9.         0, 0, NULL);  
  10.     if (unlikely(!ashmem_range_cachep)) {  
  11.         printk(KERN_ERR "ashmem: failed to create slab cache\n");  
  12.         return -ENOMEM;  
  13.     }  
  14.  
  15.     ......  
  16.  
  17.     printk(KERN_INFO "ashmem: initialized\n");  
  18.  
  19.     return 0;  

      回到ashmem_unpin函数中,我们再来看看range_alloc函数的实现:

 
 
  1. /*  
  2.  * range_alloc - allocate and initialize a new ashmem_range structure  
  3.  *  
  4.  * 'asma' - associated ashmem_area  
  5.  * 'prev_range' - the previous ashmem_range in the sorted asma->unpinned list  
  6.  * 'purged' - initial purge value (ASMEM_NOT_PURGED or ASHMEM_WAS_PURGED)  
  7.  * 'start' - starting page, inclusive  
  8.  * 'end' - ending page, inclusive  
  9.  *  
  10.  * Caller must hold ashmem_mutex.  
  11.  */  
  12. static int range_alloc(struct ashmem_area *asma,  
  13.                struct ashmem_range *prev_range, unsigned int purged,  
  14.                size_t start, size_t end)  
  15. {  
  16.     struct ashmem_range *range;  
  17.  
  18.     range = kmem_cache_zalloc(ashmem_range_cachep, GFP_KERNEL);  
  19.     if (unlikely(!range))  
  20.         return -ENOMEM;  
  21.  
  22.     range->asma = asma;  
  23.     range->pgstart = start;  
  24.     range->pgend = end;  
  25.     range->purged = purged;  
  26.  
  27.     list_add_tail(&range->unpinned, &prev_range->unpinned);  
  28.  
  29.     if (range_on_lru(range))  
  30.         lru_add(range);  
  31.  
  32.     return 0;  

       这个函数的作用是从slab 缓冲区中ashmem_range_cachep分配一个ashmem_range,然后对它作相应的初始化,放在相应的ashmem_area->unpinned_list列表中,并且还要判断这个range的purged是否是ASHMEM_NOT_PURGED状态,如果是,还要把它放在lru列表中:

 
 
  1. static inline void lru_add(struct ashmem_range *range)  
  2. {  
  3.     list_add_tail(&range->lru, &ashmem_lru_list);  
  4.     lru_count += range_size(range);  

       这样,ashmem_unpin的源代码我们就分析完了。

 

       接着,我们再来看一下ASHMEM_PIN操作,进入到ashmem_pin函数:

 
 
  1. /*  
  2.  * ashmem_pin - pin the given ashmem region, returning whether it was  
  3.  * previously purged (ASHMEM_WAS_PURGED) or not (ASHMEM_NOT_PURGED).  
  4.  *  
  5.  * Caller must hold ashmem_mutex.  
  6.  */  
  7. static int ashmem_pin(struct ashmem_area *asma, size_t pgstart, size_t pgend)  
  8. {  
  9.     struct ashmem_range *range, *next;  
  10.     int ret = ASHMEM_NOT_PURGED;  
  11.  
  12.     list_for_each_entry_safe(range, next, &asma->unpinned_list, unpinned) {  
  13.         /* moved past last applicable page; we can short circuit */  
  14.         if (range_before_page(range, pgstart))  
  15.             break;  
  16.  
  17.         /*  
  18.          * The user can ask us to pin pages that span multiple ranges,  
  19.          * or to pin pages that aren't even unpinned, so this is messy.  
  20.          *  
  21.          * Four cases:  
  22.          * 1. The requested range subsumes an existing range, so we  
  23.          *    just remove the entire matching range.  
  24.          * 2. The requested range overlaps the start of an existing  
  25.          *    range, so we just update that range.  
  26.          * 3. The requested range overlaps the end of an existing  
  27.          *    range, so we just update that range.  
  28.          * 4. The requested range punches a hole in an existing range,  
  29.          *    so we have to update one side of the range and then 
  30.          *    create a new range for the other side.  
  31.          */  
  32.         if (page_range_in_range(range, pgstart, pgend)) {  
  33.             ret |= range->purged;  
  34.  
  35.             /* Case #1: Easy. Just nuke the whole thing. */  
  36.             if (page_range_subsumes_range(range, pgstart, pgend)) {  
  37.                 range_del(range);  
  38.                 continue;  
  39.             }  
  40.  
  41.             /* Case #2: We overlap from the start, so adjust it */  
  42.             if (range->pgstart >= pgstart) {  
  43.                 range_shrink(range, pgend + 1, range->pgend);  
  44.                 continue;  
  45.             }  
  46.  
  47.             /* Case #3: We overlap from the rear, so adjust it */  
  48.             if (range->pgend <= pgend) {  
  49.                 range_shrink(range, range->pgstart, pgstart-1);  
  50.                 continue;  
  51.             }  
  52.  
  53.             /*  
  54.              * Case #4: We eat a chunk out of the middle. A bit 
  55.              * more complicated, we allocate a new range for the  
  56.              * second half and adjust the first chunk's endpoint.  
  57.              */  
  58.             range_alloc(asma, range, range->purged,  
  59.                     pgend + 1, range->pgend);  
  60.             range_shrink(range, range->pgstart, pgstart - 1);  
  61.             break;  
  62.         }  
  63.     }  
  64.  
  65.     return ret;  

        前面我们说过,被pin的内存块,必须是在unpinned_list列表中的,如果不在,就什么都不用做。要判断要pin的内存块是否在unpinned_list列表中,又要通过遍历相应的asma->unpinned_list列表来找出与之相交的内存块了。这个函数的处理方法大体与前面的ashmem_unpin函数是一致的,也是要考虑四种不同的相交情况,这里就不详述了,读者可以自己分析一下。

 

        这里我们只看一下range_shrink函数的实现:

 
 
  1. /*  
  2.  * range_shrink - shrinks a range  
  3.  *  
  4.  * Caller must hold ashmem_mutex.  
  5.  */  
  6. static inline void range_shrink(struct ashmem_range *range,  
  7.                 size_t start, size_t end)  
  8. {  
  9.     size_t pre = range_size(range);  
  10.  
  11.     range->pgstart = start;  
  12.     range->pgend = end;  
  13.  
  14.     if (range_on_lru(range))  
  15.         lru_count -= pre - range_size(range);  

        这个函数的实现很简单,只是调整一下range描述的内存块的起始页面号,如果它是位于lru列表中,还要调整一下在lru列表中的总页面数大小。

 

        这样,匿名共享内存的ASHMEM_PIN和ASHMEM_UNPIN操作就介绍完了,但是,我们还看不出来Ashmem驱动程序是怎么样辅助内存管理系统来有效管理内存的。有了前面这些unpinned的内存块列表之后,下面我们就看一下Ashmem驱动程序是怎么样辅助内存管理系统来有效管理内存的。

        首先看一下Ashmem驱动程序模块初始化函数ashmem_init:

 
 
  1. static struct shrinker ashmem_shrinker = {  
  2.     .shrink = ashmem_shrink,  
  3.     .seeks = DEFAULT_SEEKS * 4,  
  4. };  
  5.  
  6. static int __init ashmem_init(void)  
  7. {  
  8.     int ret;  
  9.  
  10.     ......  
  11.  
  12.     register_shrinker(&ashmem_shrinker);  
  13.  
  14.     printk(KERN_INFO "ashmem: initialized\n");  
  15.  
  16.     return 0;  

        这里通过调用register_shrinker函数向内存管理系统注册一个内存回收算法函数。在Linux内核中,当系统内存紧张时,内存管理系统就会进行内存回收算法,将一些最近没有用过的内存换出物理内存去,这样可以增加物理内存的供应。因此,当内存管理系统进行内存回收时,就会调用到这里的ashmem_shrink函数,让Ashmem驱动程序执行内存回收操作:

 
 
  1. /*  
  2.  * ashmem_shrink - our cache shrinker, called from mm/vmscan.c :: shrink_slab  
  3.  *  
  4.  * 'nr_to_scan' is the number of objects (pages) to prune, or 0 to query how  
  5.  * many objects (pages) we have in total.  
  6.  *  
  7.  * 'gfp_mask' is the mask of the allocation that got us into this mess.  
  8.  *  
  9.  * Return value is the number of objects (pages) remaining, or -1 if we cannot  
  10.  * proceed without risk of deadlock (due to gfp_mask).  
  11.  *  
  12.  * We approximate LRU via least-recently-unpinned, jettisoning unpinned partial 
  13.  * chunks of ashmem regions LRU-wise one-at-a-time until we hit 'nr_to_scan' 
  14.  * pages freed.  
  15.  */  
  16. static int ashmem_shrink(int nr_to_scan, gfp_t gfp_mask)  
  17. {  
  18.     struct ashmem_range *range, *next;  
  19.  
  20.     /* We might recurse into filesystem code, so bail out if necessary */  
  21.     if (nr_to_scan && !(gfp_mask & __GFP_FS))  
  22.         return -1;  
  23.     if (!nr_to_scan)  
  24.         return lru_count;  
  25.  
  26.     mutex_lock(&ashmem_mutex);  
  27.     list_for_each_entry_safe(range, next, &ashmem_lru_list, lru) {  
  28.         struct inode *inode = range->asma->file->f_dentry->d_inode;  
  29.         loff_t start = range->pgstart * PAGE_SIZE;  
  30.         loff_t end = (range->pgend + 1) * PAGE_SIZE - 1;  
  31.  
  32.         vmtruncate_range(inode, start, end);  
  33.         range->purged = ASHMEM_WAS_PURGED;  
  34.         lru_del(range);  
  35.  
  36.         nr_to_scan -= range_size(range);  
  37.         if (nr_to_scan <= 0)  
  38.             break;  
  39.     }  
  40.     mutex_unlock(&ashmem_mutex);  
  41.  
  42.     return lru_count;  

        这里的参数nr_to_scan表示要扫描的页数,如果是0,则表示要查询一下,当前Ashmem驱动程序有多少页面可以回收,这里就等于挂在lru列表的内块页面的总数了,即lru_count;否则,就要开始扫描lru列表,从中回收内存了,直到回收的内存页数等于nr_to_scan,或者已经没有内存可回收为止。回收内存页面是通过vm_truncate_range函数进行的,这个函数定义在kernel/common/mm/memory.c文件中,它是Linux内核内存管理系统实现的,有兴趣的读者可以研究一下。

 

        这样,Android系统匿名共享内存Ashmem驱动程序源代码就分析完了,在下一篇文章中,我们将继续分析Android系统的匿名共享内存机制,研究它是如何通过Binder进程间通信机制实现在不同进程程进行内存共享的,敬请关注。





本文转自 Luoshengyang 51CTO博客,原文链接:http://blog.51cto.com/shyluo/965476,如需转载请自行联系原作者

目录
相关文章
|
27天前
|
开发框架 前端开发 Android开发
Flutter 与原生模块(Android 和 iOS)之间的通信机制,包括方法调用、事件传递等,分析了通信的必要性、主要方式、数据传递、性能优化及错误处理,并通过实际案例展示了其应用效果,展望了未来的发展趋势
本文深入探讨了 Flutter 与原生模块(Android 和 iOS)之间的通信机制,包括方法调用、事件传递等,分析了通信的必要性、主要方式、数据传递、性能优化及错误处理,并通过实际案例展示了其应用效果,展望了未来的发展趋势。这对于实现高效的跨平台移动应用开发具有重要指导意义。
107 4
|
16天前
|
监控 Java Android开发
深入探索Android系统的内存管理机制
本文旨在全面解析Android系统的内存管理机制,包括其工作原理、常见问题及其解决方案。通过对Android内存模型的深入分析,本文将帮助开发者更好地理解内存分配、回收以及优化策略,从而提高应用性能和用户体验。
|
19天前
|
Java 开发工具 Android开发
安卓与iOS开发环境对比分析
在移动应用开发的广阔天地中,安卓和iOS两大平台各自占据半壁江山。本文深入探讨了这两个平台的开发环境,从编程语言、开发工具到用户界面设计等多个角度进行比较。通过实际案例分析和代码示例,我们旨在为开发者提供一个清晰的指南,帮助他们根据项目需求和个人偏好做出明智的选择。无论你是初涉移动开发领域的新手,还是寻求跨平台解决方案的资深开发者,这篇文章都将为你提供宝贵的信息和启示。
25 8
|
1月前
|
监控 Java Android开发
深入探讨Android系统的内存管理机制
本文将深入分析Android系统的内存管理机制,包括其内存分配、回收策略以及常见的内存泄漏问题。通过对这些方面的详细讨论,读者可以更好地理解Android系统如何高效地管理内存资源,从而提高应用程序的性能和稳定性。
66 16
|
21天前
|
并行计算 算法 测试技术
C语言因高效灵活被广泛应用于软件开发。本文探讨了优化C语言程序性能的策略,涵盖算法优化、代码结构优化、内存管理优化、编译器优化、数据结构优化、并行计算优化及性能测试与分析七个方面
C语言因高效灵活被广泛应用于软件开发。本文探讨了优化C语言程序性能的策略,涵盖算法优化、代码结构优化、内存管理优化、编译器优化、数据结构优化、并行计算优化及性能测试与分析七个方面,旨在通过综合策略提升程序性能,满足实际需求。
49 1
|
22天前
|
JavaScript
如何使用内存快照分析工具来分析Node.js应用的内存问题?
需要注意的是,不同的内存快照分析工具可能具有不同的功能和操作方式,在使用时需要根据具体工具的说明和特点进行灵活运用。
39 3
|
23天前
|
安全 Android开发 数据安全/隐私保护
深入探索Android与iOS系统安全性的对比分析
在当今数字化时代,移动操作系统的安全已成为用户和开发者共同关注的重点。本文旨在通过比较Android与iOS两大主流操作系统在安全性方面的差异,揭示两者在设计理念、权限管理、应用审核机制等方面的不同之处。我们将探讨这些差异如何影响用户的安全体验以及可能带来的风险。
30 1
|
1月前
|
开发框架 监控 .NET
【Azure App Service】部署在App Service上的.NET应用内存消耗不能超过2GB的情况分析
x64 dotnet runtime is not installed on the app service by default. Since we had the app service running in x64, it was proxying the request to a 32 bit dotnet process which was throwing an OutOfMemoryException with requests >100MB. It worked on the IaaS servers because we had the x64 runtime install
|
1月前
|
Android开发 开发者
Android性能优化——内存管理的艺术
Android性能优化——内存管理的艺术
|
Android开发
Android源代码下载与编译 - 2019
Android源代码的下载和编译 2019版
8770 0