Loading...
墨滴

公众号:offer多多

2021/08/22  阅读:35  主题:橙心

理解LINUX的MEMORY OVERCOMMIT

60秒问答:new 底层原理

历史回顾

  • 多次次了,每次都失败,自己感觉总结很多了【virtual 函数】

  • 下次别人提出更难问题,还是自己没理解【构造和析构顺序】

本周阅读:深度探索C++对象模型

https://mp.weixin.qq.com/s/pAoIe9m2Oat7d8c_ZW5Qyg

C++对象模型-构造函数语义学

https://mp.weixin.qq.com/s/z246VYFrR9zDzIWZTS5yWA

C++ 对象的内存布局(上) https://coolshell.cn/articles/12176.html

阅读收益

问:一个服务1G,内存,能通过malloc 申请2G内存吗?

第一步:了解参数memory overcommit含义并且测试

  1. 可以做到 man malloc

敲重点:malloc返回的非null 地址,不保证一定可用

  • The malloc() function allocates size bytes and returns a pointer to the allocated memory.

    The memory is not initialized.
    
     [没有初始化,没有分配物理内存 ,staic 变量]
    
  • By default, Linux follows an optimistic memory allocation strategy. This means that when malloc() returns non-NULL there is no guarantee that the memory really is available.
    【在 Linux 上,malloc永远不会失败。它总是返回一个指向已分配内存的指针,但稍后如果没有足够的物理内存可用,您的应用程序可能会在尝试访问该内存时崩溃。 】

  • In case it turns out that the system is out of memory, one or more processes will be killed by the OOM killer. For more information, see the description of

    /proc/sys/vm/overcommit_memory

60秒思考: 变量没有初始化 还有哪些用处?为初始化的变量就是弱符号

  • cat /proc/sys/vm/overcommit_memory 0 默认

  • 继续

0 – 这是缺省值 ,采取试试态度:OVERCOMMIT_GUESS【不行就拉到】

可以过度使用swap,意思是说可以申请过大内存,但是不能冥想过大【这个衡量标准不明确】


1 – Always overcommit. 允许overcommit,对内存申请来者不拒。【没有任何限制】

2 – Don’t overcommit. 禁止overcommit。【建议做法】
申请的内存总数超过CommitLimit的话就算是overcommit。
公式如下:
【CommitLimit = (Physical RAM * vm.overcommit_ratio / 100) + Swap】

不超过swap+物理内存的50%


0 - Heuristic overcommit handling. 【过度申请是不行的,邓家 1 】
    Obvious overcommits of
  address space are refused. 
    
    Used for a typical system. 
    Itensures a seriously wild allocation fails while allowing
  overcommit to reduce swap usage.  
    root is allowed to 
  allocate slightly more memory in this mode. 
    
    This is the 
  default.

1 - Always overcommit.【来者不拒】
    Appropriate for some scientific
  applications. Classic example is code using sparse arrays
  and just relying on the virtual memory consisting almost
  entirely of zero pages.

2 - Don't overcommit(超过意思,不是不允许申请过大的). 
 
    The total address space commit
  for the system is not permitted to exceed swap + a
    
  configurable amount (default is 50%) of physical RAM.
  Depending on the amount you use, in most situations
  this means a process will not be killed while accessing
  pages but will receive errors on memory allocation as
  appropriate.


grep -i commit /proc/meminfo
CommitLimit:     1464112 kB 【CommitLimit 就是overcommit的阈值】
Committed_AS:     608060 kB 【已经申请的Committed_As】

  • 为什么配置2 【过度使用内存属于程序问题,不应该os处理,os提示错误】

第2步: 问题还原: vm.overcommit_memory=2的测试

2.1 问题还原 内存申请分配失败现象

  • 执行任何命令都错误;bash: fork: 无法分配内存

在2ge 内存的 内存已经不够。


echo 2 >>/proc/sys/vm/overcommit_memory 

cat /proc/sys/vm/overcommit_memory 
结果执行任何sell 命令都报错

cat /proc/sys/vm/overcommit_memory 【shell fork一个进程 exec 】
 s
-bash: fork: 无法分配内存 【shell fork一个进程 exec 】



解决办法:腾讯云主机安全防护(云镜卸载)

/usr/local/qcloud/YunJing/uninst.sh

2.2 问题还原: vm.overcommit_memory=2的测试 证明 malloc 申请失败情况。【慎用】

-bash: fork: 无法分配内存


#include<stdio.h> 
#include<stdlib.h> 
#include<string.h> 
#include<unistd.h> 
int main() 

        char* p=NULL; 
        while(1) 
        { 
                p=malloc(sizeof(int)*1000); 
                if(NULL==p) 
                { 
                        break
                } 
                /*
                下面的这一句话,保证了申请到的内存都是有效的,可被使用的,在内存分配原则中,就存在一种情况是,如果分配的内存不被使用,可以预先分配并不存在的内存,等待内存被释放,
                */
                memset(p,0x00,100); 
                
                usleep(10); 
        } 
        while(1) 
        { 
                sleep(1); 
        } 
        return 0; 
}

执行任何命令都报错: 包括导致无法登陆 情况。 这个测试导致服务不可用

[root@VM-0-10-centos demo]# ./a.out 
-bash: fork: 无法分配内存

gcc 33.c
-bash: fork: 无法分配内存

free -m
-bash: fork: 无法分配内存

ls -ltra  /proc [无法删除进程]
-bash: fork: 无法分配内存

 top
-bash: fork: 无法分配内存

shutdown -r now
-bash: fork: 无法分配内存

了解swap:没有开启 vm.overcommit_memory=2的测试情况下 导致失败

  • free 查看 swap =0;

$ free -m

# 输出如下
#               total        used        free      shared  buff/cache   available
# Mem:           7976        4979         328         124        2669        2703
# Swap:             0           0           0
  • vm.overcommit_memory =2 限制很小

  • windows的虚拟内存是电脑自动设置的

  • Linux的swap分区是你装系统的时候分好的 swap是位于磁盘上的特殊文件(或分区),属于“虚拟内存”的一部分

Swap space in Linux is used when the amount of physical memory (RAM) is full

虚拟内存即所谓的 swap

  • 开启

echo 60 >/proc/sys/vm/swappiness

centos开启虚拟内存
1 mkdir /swaps

cd /swaps

3   dd if=/dev/zero of=swaps bs=512k count=4096  

 swap大小为bs*count=4096*512/1024/1024=(2G)

4  swapon /swaps/swaps

5 开机挂载

cat /etc/fstab

/swaps/swaps swap swap defaults 0 0

总结 overcommit_memory 三个情况

1 永远允许使用swap

2 超过swap+物理内存 一半 后不允许 【从设置这个最正确,但是慎用,一旦内存泄漏 导致,机器无法登陆情况.一定开启swap】

0 超过swap就失败

敲重点: 无论采取那个方式 malloc原理就是虚拟内存(盘上连续一片空间) 和overcommit_memory无关 一定开启swap:

第二步 查看代码 了解参数含义

内核参数 vm.overcommit_memory 的值0,1,2对应的源代码如下,

源文件:
https://github.com/torvalds/linux/blob/master/include/linux/mman.h
 
#define OVERCOMMIT_GUESS                0
#define OVERCOMMIT_ALWAYS               1
#define OVERCOMMIT_NEVER                2

extern int sysctl_overcommit_memory;
extern int sysctl_overcommit_ratio;

https://github.com/torvalds/linux/blob/master/mm/mmap.c

判断什么时候分配物理内存

C++ 新手一般有两个常见的误解:

  1. 任何 class 如果没有被定义 default constructor,就会被合成出来一个;【b没有构造函数】
  2. 编译器合成出来的 default constructor 会明确设定 “class 内 每一个 data member 的 默认值”.【b构造函数枚执行】

在 以下 4 种情况下,编译器会 合成 构造函数

  • case 1 当一个class 中 “带有 default constructor”的 member class object;
  • case 2 当一个class 派生自 ”带有 default constructor“的 base class;
  • case 3 当一个class 声明(或 继承)一个 virtual function;
  • case 4 当一个class 派生自一个 继承串链,其中有一个 或 多个 virtual base class.

当一个 class 中声明了 virtual function 时

  • 编译器会产生 一个 virtual function table,内放 class 的 virtual functions 地址;

  • 在每一个 class object 中,编译器 会 合成 一个 pointer member(即 vptr),用来指向 class 的 vtbl (virtual function table).

https://godbolt.org/z/P5h1Phfn4

  • C++多重继承的构造执行顺序:

构造函数的执行过程会分成两个阶段:

1 初始化阶段

  • 如果是继承的类 ,根据左到右继承顺序,
  • 如果是成员变量,类成员的上到下声明顺序

2 赋值阶段 (构造函数中代码顺序)

1.首先执行虚基类的构造函数,多个虚基类的构造函数按照被继承的顺序构造;【继承的顺序构造】

2.执行基类的构造函数,多个基类的构造函数按照被继承的顺序构造;【继承的顺序构造】

3.执行成员对象的构造函数,多个成员对象的构造函数按照声明的顺序构造;【声明的顺序】
4.执行派生类自己的构造函数;
5.析构以与构造相反的顺序执行;【相反的顺序执行】



结论
在类被构造的时候(有继承和组合),

先执行虚拟继承的父类的构造函数,

然后从左到右执行普通继承的父类的构造函数,

然后按照定义的顺序执行数据成员的初始化,

最后是自身的构造函数的调用。析构函数与之完全相反,互成镜


例子1

继承与列表初始化
下面的例子中B类继承了A和C,然后又拥有一个A和C类型的成员变量,虽然不符合设计模式,但是就将就看了。

#include <iostream>
 #include <cmath>
 using namespace std;

 class A
 {
 public:
  A(){cout << "Construct A" << endl;}
  ~A(){cout << "Destruct A" << endl;}
 };

 class C
 {
 public:
  C(){cout << "Construct C" << endl;}
  ~C(){cout << "Destruct C" << endl;}
  
 };

 class B: public A, public C
 {
 public:
  //Notice: List initialize
  B(): a(A()), c(C()) {cout << "Construct B" << endl;}
  ~B(){cout << "Destruct B" << endl;}
  C c;
  A a;
 };

 int main(int argc, char const *argv[])
 {
  B b;
  return 0;
 }

在这样的例子中输出是这样的~

  • 构造顺序

    Construct A //左到右继承顺序 Construct C //左到右继承顺序

    Construct C //类成员的上到下声明顺序 Construct A /类成员的上到下声明顺序

    Construct B //自己

    • 析构以与构造相反的顺序执行

      Destruct B //自己

    Destruct A //类成员的下到上声明顺序

    Destruct C //类成员的下到上声明顺序

    Destruct C //右到左到继承顺序

    Destruct A//右到左到继承顺序

例子2

https://www.cnblogs.com/GyForever1004/p/8439397.html

巨人肩膀

  1. https://www.etalabs.net/overcommit.html
  2. https://www.cnblogs.com/GyForever1004/p/8439397.html

公众号:offer多多

2021/08/22  阅读:35  主题:橙心

作者介绍

公众号:offer多多