学习几个“××在内存中占几份”的若干问题

时间:2022-09-21 00:12:15
  • 先需要明白一个问题:

COW:写时复制,即:Copy-on-Write,当写入时,进行拷贝:

无写入操作:共用一份内存

有写入操作:拷贝该块出来供修改,并改变进程的内存地址映射,使之映射到到新的内存块地址

这是一种内存优化策略,因为,当同一个EXE,或者同一个DLL执行的时候,程序段自然都是相同的;而在调用函数的时候压入栈的数据段,有可能也是相同的,假如确实是相同的,则操作系统会将不同的进程的相同内存块的部分映射到同一地址,其实,程序的执行无论代码或者数据,都是基于地址的,操作系统则可以通过合理的管理这些地址,进行内存空间的优化。还有另一种情况是,假如数据段被某个或者某些进程修改了怎么办呢?此时,会根据COW原则,对有修改操作的进程,将它们对该内存的控制权取消,转移到另一块内存去进行操作,避免造成共享内存数据的不一致性。关于全局函数和静态变量,他们在下面这个问题上是等价的:就是只能在一个进程内部共享,而不会和另一个进程共享,因此,不同进程对于同一个DLL中全局或者静态变量的修改,会导致系统给它专门分配一块新的空间出来。

  • 那么就有一个问题了:

如何通过一个DLL去实现进程间的数据共享呢?

有一种声明,导出,载入共享数据段的方式,但是似乎这种方法并不可行,或者说,最起码,不被推崇。

而最受欢迎的是mmap/munmap方法,多个进程,将同一个文件,映射到内存,此时,他们能(宏观上)同时对该内存进行读写操作,进而完成数据共享。

  • 虚函数,静态函数,成员函数:是否放在代码区同等的位置:

是的,都放在类作用域内(区别于对象的作用域:对象的作用域是指对象所占的堆栈空间);类的作用域空间无非是该类的常量空间,静态变量空间,代码空间(唯独没有堆栈等数据空间)。

由此也可知一个问题:内存分为堆、栈、代码、静态、常量,共五类空间。

  • static函数的问题:

另一个问题:类中static函数的问题:非static函数都会使用__thiscall方法调用,该方法实质会传入this指针作为参数,将函数中的变量重新映射到实例对应的数据区,从而对实例的成员变量进行访问;然而,static方法不会传入this指针,因此他无法访问成员函数,虚函数必须能访问虚函数表,这个表指针是存放在实例空间中的,因此虚函数不能是static方法,否则无法访问虚函数表。

  • 虚函数表具体存放在什么位置:

存放在类作用域的常量区。

  • 那些数据是放在类作用域的内存空间,那些数据是放在对象的内存空间:

成员函数代码,静态函数代码,虚函数代码,静态变量,常量,虚函数表:存放在类定义区;

虚函数表指针,成员变量:放在对象实例区。

做个实验:

修改两个不同类的“虚函数表指针”,看看是否如期望的那样:对象初始的4个字节存放“虚函数表”

代码:

 1 #include <stdlib.h>
 2 
 3 /***
 4 * @author:zanzan101
 5 */
 6 
 7 class A
 8 {
 9 private:
10     int data;
11 public:
12     virtual int foo()
13     {
14         printf("A\n");
15         return 1;
16     }
17 };
18 
19 class B : public A
20 {
21 public:
22     int foo()
23     {
24         printf("B\n");
25         return 2;
26     }
27 };
28 
29 class C : public A
30 {
31 public:
32     int foo()
33     {
34         printf("C\n");
35         return 3;
36     }
37 };
38 
39 void func(const char* str)
40 {
41     printf("%s\n", str);
42 }
43 
44 typedef void (*ptr_of_func)(const char *);    // 这是个类型定义
45 ptr_of_func p = func;                        // 这是个变量声明及初始化
46 
47 // 分开操作则报错
48 // ptr_of_func q;
49 // q = func;
50 
51 int main()
52 {
53     C c;
54     B b;
55     A* ptr = &b;
56     if(2 == ptr->foo())
57         p("多态:调用了B的函数!");
58 
59     // 修改B的对象的前4个字节的数据为C对象的前4个字节的数据
60     p("修改B的对象的前4个字节的数据为C对象的前4个字节的数据");
61     memcpy(&b, &c, sizeof(void*));
62 
63     if(3 == ptr->foo())
64         p("虚函数表指针被修改了!");
65 
66     system("pause");
67     return 0;
68 }

输出:

B
多态:调用了B的函数!
修改B的对象的前4个字节的数据为C对象的前4个字节的数据
C
虚函数表指针被修改了!
请按任意键继续. . .

 

>>转载请注明出处<<