• 九、内存管理


    一、demo

    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    
    // C语言中所有的局部变量都在栈区
    char * get_str_wrong(){
        char s[10] = "hello";  //  s 在栈区
        return s;  // 函数结束后,栈区的变量就销毁了
    }
    
    char * get_str_right(){
        char *s = malloc(10); // 在堆区申请内存,永远存在,除非手动释放
        strcpy(s,"hello");
        printf("%p
    ",s);
        return s;
    }
    
    
    int main(){
        char *s = get_str_right();
        printf("%p
    ",s);
        puts(s);
        free(s);// 释放内存
        int a = 1;  // 4 个字节    1024字节 = 1KB    1M = 1024 KB   512M  64M
        float b = 1.0; // 4 个字节
        char c = '_'; // 1 个字节
        printf("%d",sizeof(a));
        printf("%d",sizeof(b));
        printf("%d",sizeof(c));
    }

    C语言中,内存分配方法有2种:

    • 第一种:int a = 1; char s[10] = "hello"; 在栈区申请内存
    • 第二种:malloc()在堆区申请内存, free()释放内存

    Python语言中:所有的对象都在堆区申请内存

    • 释放内存,Python的内存管理器负责申请、释放内存
    • 引用计数: 记录一个对象被引用的次数
    • 垃圾回收: 当引用计数为0时,回收对象所占的内存

    Python 没有C语言省内存

    • Python 存int类型的数据: 引用的个数,类型,值
    • C语言存int类型的数据:值
    • Python用更多的内存换来了语言的易用性

    分配内存:
      底层源码与分配内存相关的函数有:
        PyObject* _PyObject_New(PyTypeObject *type);
        void* PyMem_RawRealloc(void *p, size_t n);
        void* PyMem_RawCalloc(size_t nelem, size_t elsize);
        TYPE* PyMem_New(TYPE, size_t n);
        ……
      分配内存 全部都是在堆中分配的。
      堆内存的特点(分配后,只能手动销毁) (PS: 栈内存的特点,出了作用域自动销毁)
      在Python中,Python解释器负责销毁

    引用计数:
      sys.getrefcount(obj) 可以查看obj被引用了多少次
      调用getrefcount函数时,引用+1
      +1的情况:
        - 赋值 、引用 x = obj obj = MyClass()
        - 被其他对象使用 objects = [] objects.append(obj)
        - 传给函数
      -1的情况:
        - 改变引用对象: x = obj x = 1
      - 删除 del x
      - 函数调用结束

      cpython中有个结构体,里面有个字段专门用来存计数值

    class MyClass(object):
        pass
    
    
    def f(obj):
        # print(sys.getrefcount(obj))
        pass
    
    
    def refcount_test():
        obj = MyClass()
        x = obj
        del x
        y = obj
        f(obj)
        print(sys.getrefcount(obj))
    
    
    if __name__ == '__main__':
        refcount_test()

    垃圾回收:
      引用计数为0时,垃圾回收。python解释器负责
      底层源码与垃圾回收相关的函数有:
        void PyMem_Del(void *p);
        void PyObject_Del(void *op);
        void PyMem_RawFree(void *p);

    堆内存分配在CPython 中的应用:

    在 Python 中,内存管理涉及到一个包含所有 Python 对象和数据结构的私有堆(heap),这个私有堆的管理由内部的 Python 内存管理器(Python memorymanager) 负责。
    在最底层,一个原始内存分配器通过与操作系统的内存管理器交互,确保私有堆中有足够的空间来存储所有与 Python 相关的数据。
    内存分配器根据每种对象类型的特点实现不同的内存管理策略。
    Python 堆内存的管理是由解释器来执行,用户对它没有控制权。
    先看几个从cpython 源码中的函数:

    void* PyMem_RawRealloc(void *p, size_t n); // 将 *p* 指向的内存块大小调整为 *n* 字节。
    void* PyMem_RawCalloc(size_t nelem, size_t elsize); //分配 nelem 个元素,每个元素的大小为 elsize 字节
    void* PyMem_RawMalloc(size_t n); // 分配 n 个字节,并返回一个指向分配的内存的 void* 类型指针
    void PyMem_RawFree(void *p); // 释放 p 指向的内存块。
    TYPE* PyMem_New(TYPE, size_t n); // 与 PyMem_Malloc() 相同,但分配 (n* sizeof(TYPE)) 字节的内存,返回一个转换为 TYPE* 的指针
    TYPE* PyMem_Resize(void *p, TYPE, size_t n); // 与 PyMem_Realloc() 相同,但内存块的大小被调整为 (n * sizeof(TYPE)) 字节
    void PyMem_Del(void *p); // 与 PyMem_Free() 相同

    对象的创建和销毁:

    TYPE* PyObject_New(TYPE, PyTypeObject *type); // 使用 C 结构类型 TYPE和 Python 类型对象 type 分配一个新的 Python 对象。
    void PyObject_Del(void *op); //释放内存
    PyObject* PyObject_Init(PyObject *op, PyTypeObject *type); //Return value: Borrowed reference.
    PyObject* _PyObject_New(PyTypeObject *type); // Return value: New reference.
    
    PyObject_Init 使用其类型和初始引用初始化新分配的对象操作。返回初始化的对象。new创建好的,然后进行初始化
    PyObject_New 使用C结构类型类型和Python类型对象类型分配一个新的Python对象。原创

    再看一个对象分配器:

    pymalloc // 默认内存分配器,为小对象(小于或等于 512 字节), 它使用固定大小为256 KiB 的称为 "arenas" 的内存映射
    PyMem_RawMalloc() 或 PyMem_RawRealloc() // 大于512字节的分配,刚开始创建用PyMem_RawMalloc(),动态扩容用PyMem_RawRealloc()
    
    

    Python和C语言分配内存的关系:

    pymalloc 分配器使用如下函数:

    • Windows 上的 VirtualAlloc() and VirtualFree() ,
    • mmap() 和 munmap() ,如果可用,
    • 否则, malloc() 和 free() 。

    VirtualAlloc() :Win32函数,预定指定地址和大小的虚拟内存空间。假如我们的程序要使用5 - MB内存块,但并不是要马上全部使用,则我们可以调用VirtualAlloc 函数

    VirtualFree() :释放VirtualAlloc() 分配的内存

    malloc() :C语言的内存分配函数,内部其实调用的是HeapAlloc 函数, HeapAlloc 函数是Windows提供的API,在进程初始化的时候,系统会在进程的地址空间中创建1 M大小的堆,称为默认堆(Default Heap)

    free() :释放malloc() 分配的内存

    mmap() 和 munmap() :Linux系统中的内存分配和释放函数。

    在Python中,为什么int类型是28个字节?

    import sys
    
    x = 8  # 值:8 类型:int  引用:1
    
    print(type(1)) # <class 'int'>
    print(sys.getsizeof(1))  # 28
    typedef struct {
      Py_ssize_t ob_refcnt; // 8 字节 引用计数值
      struct _typeobject *ob_type; // 8字节
      long ob_ival;// 8字节
    } PyIntObject

    思考:C语言 栈空间 和 堆空间 的区别?栈区  堆区  代码区  静态区

  • 相关阅读:
    关于 SQL Server 的事务隔离
    Joplin与阿里云OSS做同步
    vite配置vitepluginstyleimport插件后启动报错
    FireDAC组件快照
    mvn site:java.lang.NoClassDefFoundError: org/apache/maven/doxia/siterenderer/DocumentContent
    MySQL8 二进制日志
    MySQL8配置文件
    创建一个 autocomplete 输入系统 前端 + 后端
    5.Ceph 基础篇 认证
    修复 Elasticsearch 集群的常见错误和问题
  • 原文地址:https://www.cnblogs.com/zhangjx2457/p/14128431.html
Copyright © 2020-2023  润新知