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


    • 先需要明白一个问题:

    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
    ");
    15         return 1;
    16     }
    17 };
    18 
    19 class B : public A
    20 {
    21 public:
    22     int foo()
    23     {
    24         printf("B
    ");
    25         return 2;
    26     }
    27 };
    28 
    29 class C : public A
    30 {
    31 public:
    32     int foo()
    33     {
    34         printf("C
    ");
    35         return 3;
    36     }
    37 };
    38 
    39 void func(const char* str)
    40 {
    41     printf("%s
    ", 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
    虚函数表指针被修改了!
    请按任意键继续. . .

    >>转载请注明出处<<

  • 相关阅读:
    Java 验证码工具类
    Servlet实现文件下载
    SQLyog连接报错 Error No.2058 Plugin caching_sha2_password could not be loaded
    springmvc上传文件出现异常,postman测试,文件上传问题org.springframework.web.HttpMediaTypeNotSupportedException: Content type 'multipart/form-data;
    swaager2中实现文件上传的api测试操作
    idea中无法正常显示java与jsp文件内容
    servlet容器多线程与spring单例
    ThreadLocal使用与注意事项
    mysql的分组之后取时间最大的时间的那个数据
    servlet中常见装饰类HttpServletRequestWrapper等等
  • 原文地址:https://www.cnblogs.com/zanzan101/p/3383462.html
Copyright © 2020-2023  润新知