如底层系统中所述,只有共享目标文件的文本段才可供所有使用此目标文件的进程共享。目标文件的数据段通常无法共享。在数据段中写入数据项时,每个使用共享目标文件的进程都会生成一个其完整数据段的专用内存副本。可以通过把永远不会修改的数据元素移到文本段或者完全删除数据项来减小数据段。
本节介绍了几种可用于减小数据段大小的机制。
应该使用 const 声明将只读数据元素移动到文本段中。例如,以下字符串位于 .data 节中,此节属于可写数据段。
char *rdstr = "this is a read-only string";
相反,以下字符串位于 .rodata 节中,此节是文本段中的只读数据节。
const char *rdstr = "this is a read-only string";
通过将只读元素移动到文本段中来减小数据段是一种极好的方法。但是,移动需要重定位的数据元素可能会达不到预期目标。例如,请查看以下字符串数组。
char *rdstrs[] = { "this is a read-only string", "this is another read-only string" };
较好的定义可能会使用以下定义。
const char *const rdstrs[] = { ..... };
此定义可确保将字符串以及指向这些字符串的指针数组放在 .rodata 节中。遗憾的是,虽然用户将地址数组视为只读,但是在运行时必须重定位这些地址。因此,此定义会导致创建文本重定位。将此定义表示为:
const char *rdstrs[] = { ..... };
将确保在可重定位数组指针的可写数据段中维护这些指针。数组字符串将在只读文本段中维护。
可以通过折叠多重定义数据来减小数据大小。多次出现相同错误消息的程序可以通过定义全局数据来加以改进,并可使所有其他实例都引用此全局数据。例如:
const char *Errmsg = "prog: error encountered: %d"; foo() { ...... (void) fprintf(stderr, Errmsg, error); ......
进行此类数据缩减的主要目标文件是字符串。可以使用 strings(1) 查看共享目标文件中的字符串用法。以下示例在 libfoo.so.1 文件中生成数据字符串的有序表。此列表中的每项都使用字符串的出现次数作为前缀。
$ strings -10 libfoo.so.1 | sort | uniq -c | sort -rn
如果将关联的功能设计为使用自动(栈)变量,则可以完全删除数据项的永久性存储。通常,任何永久性存储删除操作都会导致所需运行时重定位数的相应地减少。
大型数据缓冲区通常应该动态分配,而不是使用永久性存储进行定义。通常,这样会从整体上节省内存,因为只分配当前调用应用程序所需的那些缓冲区。动态分配还可在不影响兼容性的情况下通过允许更改缓冲区大小来提供更大的灵活性。