【Android Linux內存及性能優化】(一) 進程內存的優化 - 堆段


一、內存篇

1.1 系統當前可用內存

1.1.1 命令 free -h

在Linux 中敲入free 命令獲得當前系統的內存使用情況。

msm8953_32:/ # free
			total        used        free      shared     buffers
Mem:       1954140160  1807552512   146587648     5267456    56188928
-/+ buffers/cache:     1751363584   202776576
Swap:               0           0           0
msm8953_32:/ # 

額,數據有點大,不好讀,我們使用 free -h 命令再來讀下:

msm8953_32:/ # free -h
				total        used        free      shared     buffers
Mem:             1.8G        1.6G        151M        5.0M         54M
-/+ buffers/cache:           1.6G        205M
Swap:               0           0           0
msm8953_32:/ # 

可以看出,當前系統使用了1.6G,空閒大小爲 151M。

在上面命令行中:
Buffers:主要用來給Linux 系統中塊設備做緩衝區。
Cached: 用來緩衝打開的文件。


1.1.2 命令 cat /proc/meminfo

/proc/meminfo 節點可以獲取當前系統實時的的詳細內存信息。

msm8953_32:/ # cat /proc/meminfo
MemTotal:        1908340 kB
MemFree:          213700 kB
MemAvailable:     957580 kB
Buffers:           56368 kB
Cached:           721008 kB
SwapCached:            0 kB
Active:           751480 kB
Inactive:         620924 kB
Active(anon):     597040 kB
Inactive(anon):     3096 kB
Active(file):     154440 kB
Inactive(file):   617828 kB
Unevictable:         256 kB
Mlocked:             256 kB
HighTotal:       1317884 kB
HighFree:         192436 kB
LowTotal:         590456 kB
LowFree:           21264 kB
SwapTotal:             0 kB
SwapFree:              0 kB
Dirty:                36 kB
Writeback:             0 kB
AnonPages:        595320 kB
Mapped:           270036 kB
Shmem:              5144 kB
Slab:              95856 kB
SReclaimable:      31192 kB
SUnreclaim:        64664 kB
KernelStack:       18608 kB
PageTables:        42732 kB
NFS_Unstable:          0 kB
Bounce:                0 kB
WritebackTmp:          0 kB
CommitLimit:      954168 kB
Committed_AS:   88266740 kB
VmallocTotal:     400384 kB
VmallocUsed:       60424 kB
VmallocChunk:      55316 kB
msm8953_32:/ # 


1.2 進程的內存使用

我們先來看下 proc 目錄下,有哪些內容:

msm8953_32:/ # ls proc/                                                        
1     16456 2260  2602  309   3606 428  599 733       consoles      
10    16605 2261  2616  31    361  429  6   734       cpu           
1011  16824 227   2633  310   362  430  60  735       cpuinfo       
10191 17    2274  2650  3103  364  431  600 736       crypto        
106   170   228   26524 311   365  432  601 737       debug         
107   171   2280  26640 3119  366  4326 602 739       device-tree   
108   1745  2285  2670  312   367  433  603 74        devices       
1094  1762  229   2685  313   369  4344 604 740       diskstats     
1096  1775  2296  27    314   37   4372 605 744       driver        
11    18    23    2701  315   370  4387 606 75        execdomains   
110   18048 230   2749  316   372  44   607 752       fb            
111   1809  23052 275   3169  373  4404 608 76        filesystems   
1123  18112 231   278   317   374  4420 609 77        fs            
113   18114 232   2788  3178  375  4442 61  79        interrupts    
114   18256 233   279   318   376  4456 612 793       iomem         
115   18257 2333  27923 319   377  4472 613 794       ioports       
116   18570 234   28    32    378  4493 615 8         irq           
117   192   2347  280   320   38   45   616 80        kallsyms      
118   1922  235   281   321   39   4510 617 808       kmsg          
119   193   2357  2814  322   393  4541 618 81        kpagecount    
12    194   236   282   3224  394  4548 619 816       kpageflags    
120   195   237   286   323   3945 46   62  82        loadavg       
121   196   2375  2867  324   3948 4652 63  822       locks         
122   197   238   287   32435 3951 47   64  824       meminfo       
123   198   239   288   325   3955 476  65  825       misc          
124   199   2390  2882  326   3967 477  652 826       modules       
125   2     24    289   327   398  48   654 827       mounts        
126   20    240   29    328   399  49   66  828       net           
127   200   2407  290   329   3990 494  661 83        pagetypeinfo  
1278  201   241   29099 33    40   495  662 84        partitions    
128   202   242   291   330   400  496  672 85        schedstat     
129   203   2424  29110 331   4001 497  68  86        scsi          
13    204   243   29111 3317  401  498  683 864       self          
130   205   244   29231 332   4012 499  684 866       softirqs      
131   206   2441  293   333   4019 5    685 867       stat          
132   20653 245   294   334   402  50   686 868       swaps         
133   207   2452  295   335   403  500  688 87        sys           
134   208   246   296   34    4038 506  689 870       sysrq-trigger 
135   209   247   297   342   404  52   69  878       thread-self   
136   21    2473  298   343   405  53   698 879       timer_list    
137   210   2478  299   344   406  54   7   88        timer_stats   
138   211   248   3     345   407  55   70  882       tty           
139   212   249   30    346   408  56   702 898       uid_cputime   
1390  213   25    300   347   4088 57   71  899       uid_io        
14    214   250   3000  348   409  58   716 9         uid_procstat  
140   215   2501  3009  349   41   584  717 90        uid_stat      
141   216   251   301   35    410  585  718 902       uptime        
1418  217   2513  3011  350   411  586  719 91        version       
142   218   2517  3014  351   4117 587  72  92        vmallocinfo   
1429  21856 252   3015  3515  412  588  720 923       vmstat        
143   219   253   302   352   4136 589  721 9281      zoneinfo          
15    222   25598 305   356   4188 593  727 asound    
1513  2222  25639 306   357   419  594  728 buddyinfo 
1514  223   2572  3068  358   42   595  729 bus       
1571  224   2582  307   359   420  596  73  cgroups   
16    225   26    308   36    4246 597  730 cmdline   
1629  226   260   3083  360   4278 598  731 config.gz 
msm8953_32:/ # 

可以看到,很多以數字命名的文件夾,其實這些都是和當前系統中運行進程的PID一一對應的。
比如 cat /proc/1/status ,我們就可以看到進程爲1 的狀態信息:

msm8953_32:/proc/1 # cat status                                                
Name:	init
State:	S (sleeping)			// 睡眠中
Tgid:	1
Pid:	1						// pid 1
PPid:	0						
TracerPid:	0
Uid:	0	0	0	0
Gid:	0	0	0	0
Ngid:	0
FDSize:	32
Groups:	3009 
VmPeak:	   16092 kB
VmSize:	   16092 kB
VmLck:	       0 kB
VmPin:	       0 kB
VmHWM:	    2664 kB
VmRSS:	    2540 kB
VmData:	    3148 kB
VmStk:	     132 kB
VmExe:	    1016 kB
VmLib:	       0 kB
VmPTE:	      20 kB
VmSwap:	       0 kB
Threads:	1
SigQ:	2/13097
SigPnd:	0000000000000000
ShdPnd:	0000000000000000
SigBlk:	0000000000000000
SigIgn:	0000000000000000
SigCgt:	0000000000010000
CapInh:	0000000000000000
CapPrm:	0000003fffffffff
CapEff:	0000003fffffffff
CapBnd:	0000003fffffffff
CapAmb:	0000000000000000
Seccomp:	0
Cpus_allowed:	ff
Cpus_allowed_list:	0-7
voluntary_ctxt_switches:	2219
nonvoluntary_ctxt_switches:	543
msm8953_32:/proc/1 # 


1.2.1 虛擬內存和物理內存

程序員在編寫程序時,不必去考慮物理內存,在32位系統中,每個進程都是“獨佔” 4GB 內存的,一臺設備上會跑數十個進程。
這些進程“獨佔”的4GB內存,稱爲 虛擬內存,操作系統屏蔽了特理內存的使用。

在Linux 內存中,採用了延遲分配物理內存的策略,針對進程的內存分配請求,程序只是在內核中分配一段虛擬地址,只有當確實需要使用這塊內存時,系統纔會分配物理地址。

例:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int main(){
	char *p = (char *)malloc(10);	// 系統給 p 分配了大小爲 10 B 的虛擬內存
	char *p1 = (char *)malloc(200);	// 系統給 p1 分配了大小爲 200 B 的虛擬內存

	strcpy(p, "123");	// 此時進程需要使用這塊內存了,內核會產生一個缺頁故障,系統纔會開始分配對應的物理頁面。
	
	// 至此,當前程序,使用的內存情況如下:
	// 虛擬內存:使用了 210 B 內存
	// 物理內存:使用了 4KB ,因爲內核分配物理內存最小單位量一個物理頁面,一個物理頁面是4KB。
	return 0;
}

1.2.2 /proc/{pid}

回到 /proc/1 中:

msm8953_32:/proc/1 # cat statm                                                 
4023 635 280 254 0 820 0
msm8953_32:/proc/1 #

我們使用命令 cat /proc/1/statm ,可以看到了7個數字,它們以頁(4KB)爲單位,其中:

4023 Size,任務虛擬地址空間的大小,4023 × 4KB
635 Resident,應用程序正在使用的物理內存大小, 635 × 4 KB
280 Shared,共享頁數,74 × 4 KB
254 Trs,程序所擁有的可執行虛擬內存大小, 254 × 4KB
0 Lrs,被映像到任務的虛擬內存空間的庫的大小
820 Drs,程序數據段和用戶態的棧大小,820 × 4KB
0 dt,髒頁數量(已修改的物理頁面),0 × 4KB

可以看到,1 號進程使用了 4023 個頁面, 也就是 4023 × 4 KB = 16092 KB = 15.71 MB

接下來,通過 cat /proc/1/maps 看下,內存詳細情況:

msm8953_32:/proc/1 # cat maps  
// 		    
// 該內存段的虛擬地址 32kb ~ 1048kb  (1016kb) , 該內存段權限爲 可讀r,不可寫w,可執行x,私有p,不共享s        
// 只讀可執行,對應 代碼段, 程序需要加載到內存纔可以執行            
00008000-00106000 r-xp 00000000 00:02 13476      /init

// 該內存段的虛擬地址 1048kb ~ 1064kb  (16kb) , 該內存段權限爲 可讀,不可寫,不可執行,私有   
// 只讀,對應 數據段,主要存儲執行文件所用到的全局變量、靜態變量
00106000-0010a000 r--p 000fd000 00:02 13476      /init

// 該內存段的虛擬地址 1048kb ~ 1052kb  (4kb) , 該內存段權限爲 可讀,可寫,不可執行,私有
0010a000-0010b000 rw-p 00101000 00:02 13476      /init
0010b000-0010d000 rw-p 00000000 00:00 0 
0010d000-0010e000 r--p 00000000 00:00 0 

// 可讀可寫,對應 堆段,進程使用malloc 申請的內存都放在這裏
0010e000-00111000 rw-p 00000000 00:00 0          [heap]
b1b00000-b1d00000 rw-p 00000000 00:00 0          [anon:libc_malloc]
// 使用到的prop 屬性
b1d20000-b1d40000 rw-s 00000000 00:0e 5242       /dev/__properties__/properties_serial
b1d40000-b1d60000 rw-s 00000000 00:0e 5241       /dev/__properties__/u:object_r:bluetooth_prop:s0
b1d60000-b1d80000 rw-s 00000000 00:0e 5240       /dev/__properties__/u:object_r:config_prop:s0
b1d80000-b1da0000 rw-s 00000000 00:0e 5239       /dev/__properties__/u:object_r:dalvik_prop:s0
b1da0000-b1dc0000 rw-s 00000000 00:0e 5238       /dev/__properties__/u:object_r:debuggerd_prop:s0
b1dc0000-b1de0000 rw-s 00000000 00:0e 5237       /dev/__properties__/u:object_r:debug_prop:s0
b1de0000-b1e00000 rw-s 00000000 00:0e 5236       /dev/__properties__/u:object_r:system_prop:s0
b1e00000-b1e20000 rw-s 00000000 00:0e 5235       /dev/__properties__/u:object_r:pan_result_prop:s0
b1e20000-b1e40000 rw-s 00000000 00:0e 5234       /dev/__properties__/u:object_r:dhcp_prop:s0
b1e40000-b1e60000 rw-s 00000000 00:0e 5233       /dev/__properties__/u:object_r:dumpstate_options_prop:s0
b1e60000-b1e80000 rw-s 00000000 00:0e 5232       /dev/__properties__/u:object_r:dumpstate_prop:s0
b1e80000-b1ea0000 rw-s 00000000 00:0e 5231       /dev/__properties__/u:object_r:radio_prop:s0
b1ea0000-b1ec0000 rw-s 00000000 00:0e 5230       /dev/__properties__/u:object_r:hwservicemanager_prop:s0
b1ec0000-b1ee0000 rw-s 00000000 00:0e 5229       /dev/__properties__/u:object_r:logpersistd_logging_prop:s0
b1ee0000-b1f00000 rw-s 00000000 00:0e 5228       /dev/__properties__/u:object_r:log_tag_prop:s0
b1f00000-b1f20000 rw-s 00000000 00:0e 5227       /dev/__properties__/u:object_r:wifi_log_prop:s0
b1f20000-b1f40000 rw-s 00000000 00:0e 5226       /dev/__properties__/u:object_r:log_prop:s0
b1f40000-b1f60000 rw-s 00000000 00:0e 5225       /dev/__properties__/u:object_r:net_radio_prop:s0
b1f60000-b1f80000 rw-s 00000000 00:0e 5224       /dev/__properties__/u:object_r:net_dns_prop:s0
b1f80000-b1fa0000 rw-s 00000000 00:0e 5223       /dev/__properties__/u:object_r:nfc_prop:s0
b1fa0000-b1fc0000 rw-s 00000000 00:0e 5222       /dev/__properties__/u:object_r:audio_prop:s0
b1fc0000-b1fe0000 rw-s 00000000 00:0e 5221       /dev/__properties__/u:object_r:persist_debug_prop:s0
b1fe0000-b2000000 rw-s 00000000 00:0e 5220       /dev/__properties__/u:object_r:device_logging_prop:s0
b2000000-b2020000 rw-s 00000000 00:0e 5219       /dev/__properties__/u:object_r:logd_prop:s0
b2020000-b2040000 rw-s 00000000 00:0e 5218       /dev/__properties__/u:object_r:mmc_prop:s0
b2040000-b2060000 rw-s 00000000 00:0e 5217       /dev/__properties__/u:object_r:netd_stable_secret_prop:s0
b2060000-b2080000 rw-s 00000000 00:0e 5216       /dev/__properties__/u:object_r:safemode_prop:s0
b2080000-b20a0000 rw-s 00000000 00:0e 5215       /dev/__properties__/u:object_r:persist_dpm_prop:s0
b20a0000-b20c0000 rw-s 00000000 00:0e 5214       /dev/__properties__/u:object_r:overlay_prop:s0
b20c0000-b20e0000 rw-s 00000000 00:0e 5213       /dev/__properties__/u:object_r:serialno_prop:s0
b20e0000-b2100000 rw-s 00000000 00:0e 5212       /dev/__properties__/u:object_r:boottime_prop:s0
b2100000-b2120000 rw-s 00000000 00:0e 5211       /dev/__properties__/u:object_r:fingerprint_prop:s0
b2120000-b2140000 rw-s 00000000 00:0e 5210       /dev/__properties__/u:object_r:vold_prop:s0
b2140000-b2160000 rw-s 00000000 00:0e 5209       /dev/__properties__/u:object_r:persistent_properties_ready_prop:s0
b2160000-b2180000 rw-s 00000000 00:0e 5208       /dev/__properties__/u:object_r:firstboot_prop:s0
b2180000-b21a0000 rw-s 00000000 00:0e 5207       /dev/__properties__/u:object_r:shell_prop:s0
b21a0000-b21c0000 rw-s 00000000 00:0e 5206       /dev/__properties__/u:object_r:restorecon_prop:s0
b21c0000-b21e0000 rw-s 00000000 00:0e 5205       /dev/__properties__/u:object_r:cppreopt_prop:s0
b21e0000-b2200000 rw-s 00000000 00:0e 5204       /dev/__properties__/u:object_r:powerctl_prop:s0
b2200000-b2220000 rw-s 00000000 00:0e 5203       /dev/__properties__/u:object_r:system_radio_prop:s0
b2220000-b2240000 rw-s 00000000 00:0e 5202       /dev/__properties__/u:object_r:ffs_prop:s0
b2240000-b2260000 rw-s 00000000 00:0e 5201       /dev/__properties__/u:object_r:default_prop:s0
b2260000-b2280000 rw-s 00000000 00:0e 5200       /dev/__properties__/u:object_r:wifi_prop:s0
b2280000-b22a0000 rw-s 00000000 00:0e 5199       /dev/__properties__/u:object_r:camera_prop:s0
b22a0000-b22c0000 rw-s 00000000 00:0e 5198       /dev/__properties__/u:object_r:debug_gralloc_prop:s0
b22c0000-b22e0000 rw-s 00000000 00:0e 5197       /dev/__properties__/u:object_r:boot_animation_prop:s0
b22e0000-b2300000 rw-s 00000000 00:0e 5196       /dev/__properties__/u:object_r:dolby_prop:s0
b2300000-b2320000 rw-s 00000000 00:0e 5195       /dev/__properties__/u:object_r:fm_prop:s0
b2320000-b2340000 rw-s 00000000 00:0e 5194       /dev/__properties__/u:object_r:location_prop:s0
b2340000-b2360000 rw-s 00000000 00:0e 5193       /dev/__properties__/u:object_r:media_msm8953_version_prop:s0
b2360000-b2380000 rw-s 00000000 00:0e 5192       /dev/__properties__/u:object_r:netd_prop:s0
b2380000-b23a0000 rw-s 00000000 00:0e 5191       /dev/__properties__/u:object_r:graphics_vulkan_prop:s0
b23a0000-b23c0000 rw-s 00000000 00:0e 5190       /dev/__properties__/u:object_r:xlat_prop:s0
b23c0000-b23e0000 rw-s 00000000 00:0e 5189       /dev/__properties__/u:object_r:rmnet_mux_prop:s0
b23e0000-b2400000 rw-s 00000000 00:0e 5188       /dev/__properties__/u:object_r:crash_prop:s0
b2400000-b2420000 rw-s 00000000 00:0e 5187       /dev/__properties__/u:object_r:fst_prop:s0
b2420000-b2440000 rw-s 00000000 00:0e 5186       /dev/__properties__/u:object_r:mmi_prop:s0
b2440000-b2460000 rw-s 00000000 00:0e 5185       /dev/__properties__/u:object_r:vendor_system_prop:s0
b2460000-b2480000 rw-s 00000000 00:0e 5184       /dev/__properties__/u:object_r:wigig_prop:s0
b2480000-b24a0000 rw-s 00000000 00:0e 5183       /dev/__properties__/u:object_r:qemu_gles_prop:s0
b24a0000-b24c0000 rw-s 00000000 00:0e 5182       /dev/__properties__/u:object_r:qemu_hw_mainkeys_prop:s0
b24c0000-b24e0000 rw-s 00000000 00:0e 5181       /dev/__properties__/u:object_r:mpdecision_prop:s0
b24e0000-b2500000 rw-s 00000000 00:0e 5180       /dev/__properties__/u:object_r:alarm_boot_prop:s0
b2500000-b2520000 rw-s 00000000 00:0e 5179       /dev/__properties__/u:object_r:alarm_handled_prop:s0
b2520000-b2540000 rw-s 00000000 00:0e 5178       /dev/__properties__/u:object_r:alarm_instance_prop:s0
b2540000-b2560000 rw-s 00000000 00:0e 5177       /dev/__properties__/u:object_r:freq_prop:s0
b2560000-b2580000 rw-s 00000000 00:0e 5176       /dev/__properties__/u:object_r:hwui_prop:s0
b2580000-b25a0000 rw-s 00000000 00:0e 5175       /dev/__properties__/u:object_r:opengles_prop:s0
b25a0000-b25c0000 rw-s 00000000 00:0e 5174       /dev/__properties__/u:object_r:usf_prop:s0
b25c0000-b25e0000 rw-s 00000000 00:0e 5173       /dev/__properties__/u:object_r:radio_noril_prop:s0
b25e0000-b2600000 rw-s 00000000 00:0e 5172       /dev/__properties__/u:object_r:sf_lcd_density_prop:s0
b2600000-b2620000 rw-s 00000000 00:0e 5171       /dev/__properties__/u:object_r:vendor_coresight_prop:s0
b2620000-b2640000 rw-s 00000000 00:0e 5170       /dev/__properties__/u:object_r:reschedule_service_prop:s0
b2640000-b2660000 rw-s 00000000 00:0e 5169       /dev/__properties__/u:object_r:bservice_prop:s0
b2660000-b2680000 rw-s 00000000 00:0e 5168       /dev/__properties__/u:object_r:scr_enabled_prop:s0
b2680000-b26a0000 rw-s 00000000 00:0e 5167       /dev/__properties__/u:object_r:sdm_idle_time_prop:s0
b26a0000-b26c0000 rw-s 00000000 00:0e 5166       /dev/__properties__/u:object_r:boot_mode_prop:s0
b26c0000-b26e0000 rw-s 00000000 00:0e 5165       /dev/__properties__/u:object_r:qcom_ims_prop:s0
b26e0000-b2700000 rw-s 00000000 00:0e 5164       /dev/__properties__/u:object_r:nfc_nq_prop:s0
b2700000-b2720000 rw-s 00000000 00:0e 5163       /dev/__properties__/u:object_r:sys_usb_configfs_prop:s0
b2720000-b2740000 rw-s 00000000 00:0e 5162       /dev/__properties__/u:object_r:sys_usb_controller_prop:s0
b2740000-b2760000 rw-s 00000000 00:0e 5161       /dev/__properties__/u:object_r:sys_usb_tethering_prop:s0
b2760000-b2780000 rw-s 00000000 00:0e 5160       /dev/__properties__/u:object_r:uicc_prop:s0
b2780000-b27a0000 rw-s 00000000 00:0e 5159       /dev/__properties__/u:object_r:bg_boot_complete_prop:s0
b27a0000-b27c0000 rw-s 00000000 00:0e 5158       /dev/__properties__/u:object_r:bg_daemon_prop:s0
b27c0000-b27e0000 rw-s 00000000 00:0e 5157       /dev/__properties__/u:object_r:vendor_qdcmss_prop:s0
b27e0000-b2800000 rw-s 00000000 00:0e 5156       /dev/__properties__/u:object_r:per_mgr_state_prop:s0
b2800000-b2820000 rw-s 00000000 00:0e 5155       /dev/__properties__/u:object_r:qdma_prop:s0
b2820000-b2840000 rw-s 00000000 00:0e 5154       /dev/__properties__/u:object_r:qvr_prop:s0
b2840000-b2860000 rw-s 00000000 00:0e 5153       /dev/__properties__/u:object_r:vendor_rild_libpath_prop:s0
b2860000-b2880000 rw-s 00000000 00:0e 5152       /dev/__properties__/u:object_r:spcomlib_prop:s0
b2880000-b2980000 rw-p 00000000 00:00 0          [anon:libc_malloc]
b2991000-b29b1000 rw-s 00000000 00:0e 5151       /dev/__properties__/u:object_r:wififtmd_prop:s0
b29b1000-b29b2000 r--p 00000000 00:00 0          [anon:atexit handlers]
b29b2000-b29b3000 ---p 00000000 00:00 0          [anon:thread signal stack guard page]
b29b3000-b29b7000 rw-p 00000000 00:00 0          [anon:thread signal stack]
b29b7000-b29b8000 rw-p 00000000 00:00 0          [anon:arc4random data]
b29b8000-b29b9000 ---p 00000000 00:00 0          [anon:bionic TLS guard]
b29b9000-b29bc000 rw-p 00000000 00:00 0          [anon:bionic TLS]
b29bc000-b29bd000 ---p 00000000 00:00 0          [anon:bionic TLS guard]

// 可讀可寫,對應 棧段
bea7f000-beaa0000 rw-p 00000000 00:00 0          [stack]
bee76000-bee77000 r-xp 00000000 00:00 0          [sigpage]
ffff0000-ffff1000 r-xp 00000000 00:00 0          [vectors]
msm8953_32:/proc/1 # 

數據格式爲 
00008000-00106000	(虛擬地址) 
r-xp	(內存權限r讀/w寫/x執行/s共享/p私有) 
00000000(在進程中的地址偏移量) 
00:02 	(映像文件 主設備號:次設備號)
13476  	(映像文件 節點號)
/init	(映像文件路徑)


1.3 進程內存優化

進程內存優化主要分如下幾方面:

  1. 執行文件所佔用的內存
  2. 動態庫對內存的影響
  3. 線程對內存的影響

1.3.1 執行文件

一個進程運行時,所佔用的內存,可以分爲如下幾個部分:

  1. 棧區stack:由編譯器自動分配釋放,存放函數的參數、局部變量等。
  2. 堆區heap:一般由程序員分配釋放,若程序員不釋放,程序結束時可由操作系統來回帳。
  3. 全局變量、靜態變量
    初始化的全局變量和靜態變量在一塊區域,
    未初始化的全局變量和未初始化的靜態變量在相鄰的另一塊區域,
    程序結束後,由系統釋放。
  4. 文字常量: 常量、字符串,程序結束後由系統釋放。
  5. 程序代碼段:存放函數體的二進制代碼。

一般來說:

  1. 堆段:每個進程有一個,不論是主進程、動態庫還是不同的線程裏申請的堆內存,都反映在進程的堆段。
  2. 棧段:每個線程有一個,如果進程中有多個線程,則包含多個棧段。
  3. 代碼段:由於其只讀,不會被修改,其在整個系統內共享。
    比如,一個可執行文件,在系統中同時存在多個進程,那麼這些進程將共享其代碼段所點用的內存。
  4. 數據段: 由於它是可寫的,其內容與所在進程高度相關,故每個進程都有一個數據段。

1.3.1.1 堆段

malloc、free、new、delete 是從程序的角度去看待和使用堆內存,
而在Linux 內核中則專門爲進程分配一段內存地址,用來存放堆的內容。

隨着進程申請內存的增加,進程會通過系統調用brk 調高堆頂地址,擴展這段內存空間,從而Linux 內核分配給進程更多的內存;
當進程釋放內存時,進程又會通過系統調用brk調低堆頂地址,縮減這段內存空間,Linux內核便會將其一部分物理內存進行回收。

但考慮到效率和性能的問題
(1) 用戶態進程申請內存是以字節爲單位,而在內核中內存管理是以頁面(4kb)爲單位。
(2) 太多的系統調用,會使進程的速度變得很慢。

所以,並不是每次的malloc 和 new 都會觸發系統調用brk 去調整堆頂地址,內存管理需要在效率和空間方面做出平衡。
需要完成如下工作:
(1)接收程序的內存請求,將其積少成多,然後統一向Linux 內核申請內存。
(2)要想辦法避免頻繁的系統調用導致進程速度變慢。
(3)減少內存碎片的產生。


如下是程序調用 malloc 時,glibc 內置的內存管理器的內存分配過程。
可以看出,malloc 的內存是通過 brk 來調高進程堆頂地址,但此時並沒有分配對應的物理頁面。
而是等進程要訪問的時候,再來分配物理頁面,在進程頁表中建立映射關係。
在這裏插入圖片描述


1.3.1.1.1 小內存塊分配

在glibc 的內存管理中,它會跟蹤每一塊內存的分配和釋放,爲了減少內存碎片,
還會使用一定的算法,對相鄰的空閒內存進行合併。
而爲了提高速度,glibc 的內存管理器實現了 fastbins 算法,對於一些小塊的內存不會嘗試合併,以節省CPU 和 內存,但這些小內存可以被複用,其代價就是會增加進程中的內存碎片,從而佔用更多的內存

M_MXFAST 定義了 fastbins 的小塊內存閾值,小於該閾值的小塊空閒內存將不會去嘗試合併,其缺省值爲 64 個字節。
mallopt(M_MXFAST, 0);
可以通過將 M_MAXFAST 設置爲0, 禁止fastbins ,使其跟蹤每一個小塊內存並嘗試進行合併。


1.3.1.1.2 大內存塊分配

當進程向glibc 申請內存時,如果申請的內存數量大於一個閾值的時候,glibc 會採用 mmaps 爲進程分配一塊虛擬地址空間(會新創建一個內存段),而不是採用brk,來擴展堆頂的指針。

可以通過 mallopt 來配置閾值。
參數爲 M_MMAP_THRESHOLD、M_MMAP_MAX, 以字節爲單位。

M_MMAP_THRESHOLD 是glibc 中大塊內存閾值,大於該閾值的內存申請,內存管理器將使用mmap 系統調用申請內存;
如果小於該閾值的內存申請,內存管理其使用 brk 系統調用來擴展堆頂指針。該閾值缺省爲 128KB。

M_MMAP_MAX 是該進程中最多使用mmap 分配地址段的數量。


1.3.1.1.3 內存釋放

基於性能的考慮,爲了預防進程頻繁的申請和釋放頁面,glibc 在收到free 消息時,並不會立即將該物理頁面釋放,而是緩存起來供下次分配內存使用,只有當堆頂有連續的128KB 空閒內存時,glibc 纔會調用brk,來通知內核釋放這段內存,將其返還給系統。

  1. M_TRIM_THRESHOLD 堆頂內存回收閾值
    可以通過 mallopt 來配置閾值。
    參數爲 M_TRIM_THRESHOLD、M_TOP_PAD。 以字節爲單位。

    M_TRIM_THRESHOLD 是堆頂內存回收閾值,當堆頂連續空閒內存數量大於該閾值時,glibc 的內存管理將調用系統調用 brk, 來調整堆頂地址,釋放內存,該值缺省爲 128 KB。

    M_TOP_PAD 決定了當 glibc 內存管理器調用brk 釋放內存時,堆頂還需要保留的空閒數量,該值缺省爲0。

  2. TRIM_FASTBINS是否對fastbin 進行合併
    另外一個參數 TRIM_FASTBINS,它決定了當前釋放一個小塊內存時,是否立即對fastbin 進行合併,
    設置爲1 會進行立即合併並可以減少內存消耗,但降低分配釋放效率。

    TRIM_FASTBINS 與堆頂內存回收存在着一個接口:
    TRIM_FASTBINS=0,當小於或等於MXFAST 的小塊內存釋放時,並不會觸發堆頂內存釋放,堆頂內存釋放被延遲到大於MXFAST 的內存釋放時觸發,這樣有可能會增加內存碎片。
    TRIM_FASTBINS=1,小於MXFAST 的小塊內存釋放,也將觸發堆頂內存釋放。

如果想小塊內存也觸發堆頂內存釋放,有兩種方法:
(1)加上 DTRIM_FASTBINS = 1,重新編譯 glibc庫
(2)調整 MXFAST 的值,將其設置爲0,使得所有內存分配的大小都 將大於MXFAST。


1.3.1.1.4 內存空洞

堆內存釋放過程中是從堆頂開始的,那如果堆中間的一塊區域,大部分內存都釋放了,堆頂還有一些內存未釋放。

針對這樣的情況,主要有兩種方法:

  1. 通過使用系統調用 brk,來改變堆頂地址釋放內存。
    優點: 算法簡單,系統調用少,效率高
    缺點:堆頂下方的物理頁面即使空閒也無法及時 釋放。

  2. 通過將對應堆的線性區拆分,將中間的物理頁面釋放,這樣可能產生多個堆段。
    優點:堆頂下方的內存能夠得到及時釋放。
    缺點:算法複雜,涉及線性區的拆分與合併,有可能會導致進程堆段形成多個不連續的小塊內存空間,對進程的性能影響較大。

綜合以上因素,LInux 內核選擇了通過調整堆頂來擴展和釋放內存空間。這也就決定了只要堆頂部還有內存在使用,堆頂下方無論釋放了多少內存都不會被釋放。
如果堆頂下方存在很大一塊連續的空閒內存時,我們便稱之爲內存空洞。


前面說過,分配內存除了擴展堆頂內存外,另一種方式是 mmap。
所以,也可以通過降低mmap 分配內存的閾值,使更多的內存採用 mmap 方式分配,減小內存空洞的概率。其代價是可能會使用更多的系統調用,降低了進程的性能。


要想消除內存空洞的影響 ,就應該在申請和釋放內存時,嚴格依照就近原則,最先釋放堆頂地址的內存。但控制內存的申請和釋放的順序難度很大,另外由於碎片的影響,每次申請得到的內存地址都帶有一定的隨機性,後面申請的內存,並不一定就意味着在堆頂,因此嚴格地做到內存從堆頂開始釋放是不可能的。


內存空洞和內存泄漏造成的內存增長是有區別的:
內存泄漏是申請了內存,沒有釋放,如果你再多次做同樣的操作,進程的使用的內存應該保持同樣的一個速度進行增長。
內存空洞是申請並釋放了的內存,由於不處於堆頂無法返還給系統,但這些內存還是能留給進程自身使用的,所以如果做多次同樣的操作,進程所使用的內存應該還停留在一個水平線上,不增長或者增長不多。


1.3.1.1.5 堆內存管理器參數總結

在進程中,可能調用 mallopt 函數,來調整 glibc 的內存管理器的行爲。

#include <malloc.h>
int mallopt(int param, int value)

value 的單位是字節。
param 的取值範圍是:

  1. M_TRIM_THRESHOLD 堆頂內存回收閾值,大於它時glibc 內存管理將系統調用brk,來調整堆頂地址,釋放內存,缺省爲128KB

  2. M_TOP_PAD 調用brk 釋放內存時,堆頂保留空閒內存數量,缺省爲0

  3. M_MMAP_THRESHOLD 大塊內存閾值,大於它時,會使用mmap 系統調用申請內存,小於它時,使用brk 系統調用來擴展堆頂指針。缺省爲 128KB

  4. M_MMAP_MAX 該進程中,最多使用mmap 分配地址段的數量。

  5. M_MXFAST 定義了fastbins 的小塊內存閾值,小於該值的小塊空閒內存不會嘗試去合併,缺省是 64B


1.3.1.2 棧段

本文開始,見 《【Android Linux內存及性能優化】(二) 進程內存的優化 - 棧段

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章