九、内存管理

一、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语言 栈空间 和 堆空间 的区别?栈区  堆区  代码区  静态区

原文地址:https://www.cnblogs.com/zhangjx2457/p/14128431.html