精华内容
下载资源
问答
  • 准备回顾一下python源代码,不过不准备说太细,尽量勾勒框架,不引用代码。python中所有东西都是对象,进一步地,这些...每个对象头部都有一个PyObject_HEAD(其实对于某些需要被gc管理的对象,它头部先为PyGC_...

    准备回顾一下python源代码,不过不准备说的太细,尽量勾勒框架,不引用代码。

    python中所有东西都是对象,进一步地,这些对象可以分为类型对象(type)or实例对象,有时一个对象即可以是类型,也可以是实例。所有这些对象中,除了内置的类型对象外,别的都生存于堆上,内置的类型对象则静态分配内存。

    每个对象头部都有一个PyObject_HEAD(其实对于某些需要被gc管理的对象,它的头部先为PyGC_Head,再为PyObject_HEAD)。变长对象在HEAD后还有一个ob_size表示变长对象元素个数的多少,非字节数。

    类型的信息都在它的type对象里,源码中为struct _typeobject,也就是PyTypeObject。比如实例化一个类型,那会先找它的tp_new(找不到的话在父类找),在tp_new中根据该type的tp_basesize进行分配内存,再调用tp_init进行初始化。对类型的实例做运算,比如相加其实也是找type对象中相应的函数指针。type对象中的信息到后来基本都会在类型的dict中和相应的key对应起来。

    下面分析具体的类型。

    int:比较简单,关键在于如何高效地实现。python首先有小整数对象。默认在[-5, 257)。如果超出范围则使用通用的缓冲池,对于大整数则有PyIntBlock,用来作缓冲池。一个block大小大概为1000个字节,去掉头部(8字节),可以存82个整数对象。block之间通过指针相连,首指针为block_list,free_list则维护着一条可以链表,free_list链表的下一项由未用的PyIntObject的ob_type来维持。

    一些细节:当无可以用缓冲池可用时python会调用fill_free_list来创建一个新的block,并将其插入block_list,再把free_list指向这个block的objects中的最后一个元素。当某个block中的某个int被释放时,它将自己的ob_type指向free_list,并修改free_list等于它的地址,其实就是一个头部插入,这样把多个block间的objects数组联系起来防止出现内存泄漏。一个值得注意的地方是小整数对象池其实也是生活在block里面,在是整个python环境初始化的时候生成。这里可以看出,为int分配的内存是永远也不会被python释放的,所有的int对象使用的内存大小和同时存在的int数量的最大值有关。

    string:复杂一些,变长对象。对变长对象内存的管理。每个string对象除了头部外还保存了hash值(ob_shash,避免重复计算,初始-1)、是否已经被intern机制处理过(ob_sstate)、指向实际内存的指针(ob_sval),ob_sval指向的应该是一段ob_size+1长度的内存(为了兼容C,字符串要以'\0'结尾)。在从char *创建string时还是比较直接的,就是检查一些边界情况、初始化hash等,最后逐个拷贝char。python中有一个nullstring指向空字符串,通过intern机制共享,所以不会同时存在多个空字符串。

    传统缓冲池。相当于int小整数缓冲池,对单个的char,python也会维持一个缓冲池。创建单个char的string时,如果缓冲池里已有,则直接返回。如果没有,根据char创建string,再对它进行intern,再存入缓冲池。

    intern机制。python会维持一个dict,用来保存当前已经被创建的string,如果新创建的string已经在这个dict,也就是已经被intern机制处理过了,那么就会直接返回dict中的值。也就是说一般两个相同的字符串的id是相同的。要注意的是,无论字符串有没有存在于这个dict中,python都会创建一个新要string,原因是因为保存在dict中只能是PyObject,因此肯定要创建一个python对象。intern后的string有两种状态,mortal和immortal,区别在于后者永远不会被gc回收。

    要提到的是,创建string时使用的是PyObject_Malloc开头的分配函数,一般来讲它不会每次都从os分配内存,而是从python维持的一个内存池中分配。

    list:不仅是变长对象,还是可变对象。在变长头部之外PyListObject还保存了一个PyObject **ob_item指针,一个int allocated。ob_item就是指向实际成员的指针,allocated代表了list当前申请的内存能装多少个PyObject,变长头内的ob_size则代表list中已有多少个PyObject。当创建一个list时,需要指定list的大小(参数size),要申请的内存可以分为两部分,一个是list本身,一个是指向成员变量的指针。如果list缓冲池可用(numfree > 0),那就从缓冲池中给list分配一块内存,否则使用PyObject_GC_NEW来分配空间(和string不同,因为list中的成员可以指向其它python对象,这个函数和python垃圾回收的三色标记法有关)。然后再根据size大小分配一段连续的内存来保存指向各个成员的指针,新创建的list中的ob_size和allocated都为size。

    创建list后给list里的某个位置赋值比较简单,就是简单的設定指针而已。添加操作要复杂一些,首先会调整list的大小,使得allocate>=size>=allocate/2。如果该等式已经满足,那么不更改list大小,如果不满足的话通过new_allocated = (newsize >> 3) + (newsize < 9 ? 3 : 6) + newsize得出新的allocated大小,再通过PyMem_Resize来调整保存成员指针的内存。resize后则使用最简单的策略移动从插入到结尾的成员指针,再设置该位置上的成员。删除成员时实际删除后的也要进行resize操作,和插入时类似。实际删除的操作则由list_ass_slice来实现,它有两种用法,一是更改list中的某一段为特定的值,另一种就是删除list中的某一段。list中每删除一个元素都会造成内存拷贝。一个值得注意的细节是由于从list中删除或是更改的对象需要减少引用计数,但减少引用计数时又会循环调用一些list函数,可能会造成list索引值的破坏,因而函数中得用一个temp数组保留从list中剥离的成员,等删除工作完成,list结构已经确定的情况下再逐一减少其引用。

    当list被销毁时,对其成员逐个减少引用,然后检查缓冲区是否已经满,如果没有的话就将该list放入缓冲区,这样下次再创建list的时候就不用为list对象本身再分配对象了。

    dict:python中的dict是用hash表实现,解决冲突的方法是开放定址法,即二次探测,删除时使用伪删除技术(顺便说下在一致性假设下,如果用k来表示hash表的使用率的话,那么一次成功查找需要的比较次数为1/k * ln(1/(1-k)),插入时的比较次数最多为1/(1-k))。

    在一个dict中,每个(key,value)对组成一个entry,entry中还另外存储了key的hash值。对每个entry来说有三种状态unused(key,value皆为NULL,初始状态),active(key,value不为NULL,存储了元素),dump(对应于伪删除技术,key=dummy,value=NULL)。dict实际上就是entry的集合,定义如下:

    typedef struct _dictobject PyDictObject;

    struct _dictobject {

    PyObject_HEAD

    Py_ssize_t ma_fill;  /* # Active + # Dummy */

    Py_ssize_t ma_used;  /* # Active */

    /* The table contains ma_mask + 1 slots, and that's a power of 2.

    * We store the mask instead of the size because the mask is more

    * frequently needed.

    */

    Py_ssize_t ma_mask;

    /* ma_table points to ma_smalltable for small tables, else to

    * additional malloc'ed memory.  ma_table is never NULL!  This rule

    * saves repeated runtime null-tests in the workhorse getitem and

    * setitem calls.

    */

    PyDictEntry *ma_table;

    PyDictEntry *(*ma_lookup)(PyDictObject *mp, PyObject *key, long hash);

    PyDictEntry ma_smalltable[PyDict_MINSIZE];

    };

    PyDict_MINSIZE一般被设为8。使用PyDict_New来创建一个新dict时,其实就是分配相应的内存,将ma_fill,ma_used设为0,ma_mask设为7(8-1),ma_table指向ma_smalltable,ma_lookup默认为lookdict_string。在实现dict时python也使用了缓冲池,方法和list基本一样。

    ma_lookup这个函数指针确定了散裂函数和冲突发生时的二次散裂函数,在dict中进行搜索有两种方法,lookdict_string和lookdict,后一种是更一般的方法。由于python中dict用的非常广泛,而这些dict中大多数key都是string,因而专门提供了一个搜索方法来进行加速,其实两种方法的本质都是一样的。搜索的具体过程如下:1. 获得探测链中当前应该检测的entry;2. 如果是unused状态,那搜索失败,应该返回一个可用的(可以被设置值)的entry,所以如果freeslot不为空(之前找到了dummy态的entry)就返回freeslot指向的entry,否则返回当前entry;3. 如果entry为dummy态且freeslot未设置,则设置freeslot,继续查找下一个;4. 依次检查当前entry的key和查找的key是否引用相同、值相同,若不是继续查找下一个。需要注意的是,dict使用哪种方法进行查找取决于待查找的key,如果是string的话则利用lookdict_string,和本身已经有的entry中的key无关。

    dict的插入和删除基于之前的搜索,很好理解。当插入时先搜索该key,得到一个entry,再根据它的状态来修改它达到插入的上目的。删除操作也类似。需要注意的是插入元素的时候,会检查ma_fill/(ma_mask+1)是否大于2/3,如果装载率的确大于这个值并且有unused或dummy的entry被填充的时候,就会调整dict的大小,新的大小最小为minsize=ma_userd*(ma_used>50000?2:4),显然改变dict大小会造成内存移动,因此这时候可以丢弃dummy的entry。新的dict大小从8开始逐次*2增长,直到大于minsize。接下来就是一些初始化动作,逐个检查entry并插入新的dict等。

    在微博上关注:

    新浪,

    腾讯

    投稿

    最新招聘

    657

    展开全文
  • OpenStack存储管理功能介绍 技术创新变革未来 前言 OpenStack提供多种类型存储服务用户可以根据业务需求自由选择存储服 务 本章节重点介绍OpenStack中块存储服务Cinder 简单介绍对象存储服务Swift 本章节分为两...
  • windows的对象管理

    2011-05-12 18:01:00
     今天在调试一个SSDT的时候,我做了ZwSetValueKey的hook,本来希望在hook中将数值修改一下。...我才想到,我在内核中声明的变量地址与hook中对象的句柄不在同一层。所以一直出现访问违例的现象。对于Windows系

       今天在调试一个SSDT的时候,我做了ZwSetValueKey的hook,本来希望在hook中将数值修改一下。以达到最终的效果,但在尝试的时候,总是出错。报0xC0000005的访问违例错误。我尝试了修改数据类型,将原来的数据类型修改为REG_DWORD结果还是不行,后来在调试过程中发现了MmUserProbeAddress。该函数功能是用于检查地址是否属于ring3的地址。我才想到,我在内核中声明的变量地址与hook中对象的句柄不在同一层。所以一直出现访问违例的现象。对于Windows系统的对象如何处理的,还带进一步处理。

    展开全文
  • Java对象内存管理

    2019-07-11 15:11:00
    编译好的Java代码要放在JVM内执行,不同的数据放在不同的区域内。...由上可以得知,成员变量肯定存在于堆中,因为它是属于new 出来的对象的。 方法区:存放类的信息。Java程序运行的时候,Java类加载器会把cl...

    编译好的Java代码要放在JVM内执行,不同的数据放在不同的区域内。具体包括:堆,栈,方法区。

    堆:存放new  出来的  对象。

    栈:存放方法中的局部变量。所以,它是依赖方法的调用存在的。一旦这个方法调用完毕,它(对应的“栈帧”)就会被清除。

    由上可以得知,成员变量肯定存在于堆中,因为它是属于new  出来的对象的。

    方法区:存放类的信息。Java程序运行的时候,Java类加载器会把class字节码装载进来,然后解析之后装入方法区。

    转载于:https://www.cnblogs.com/LightmanLu/p/11170132.html

    展开全文
  • 本例程讨论类中static成员变量和static成员函数特殊之处:...4 不定义类对象使用类静态成员变量结论:静态成员变量和静态成员函数是整个类资源,不属于某个对象。补充:1 static成员函数不可以声明为const和v...

    本例程讨论类中static成员变量和static成员函数的特殊之处:

    1 静态成员函数无法调用普通成员变量
    2 静态成员变量要在类外初始化 int classXX::c = 10 ;
    3 静态成员变量可以使用类的作用域来调用 BB::getC() ;
    4 不定义类对象使用类的静态成员变量
    结论:静态成员变量和静态成员函数是整个类的资源,不属于某个对象。

    补充:

    1 static成员函数不可以声明为const和virtual

    #include<iostream>
    
    using namespace std ;
    
    class BB
    {
    public:
    	static void getC()//静态成员函数
    	{
    		cout << "c: " << c << endl ;  
    		//1 在静态成员函数中,不能调用,普通成员属性,或普通成员函数
    	//	cout << "a: " << a << endl ;  
    	}
    
    	void AddC()
    	{
    		c = c + 1 ;
    	}
    
    	void printC()
    	{
    		cout << " c = " << c << endl ;
    	}
    
    private:
    	int a ;
    	int b ;
    public:
    	static int c ;  //静态成员变量
    };
    
    //静态函数中只能使用整个类(static)的资源
    //2 静态成员变量要在类外初始化 int classXX::c = 10 ;
    int BB::c = 10 ;
    
    
    void main ()
    {
    	BB::c++;		//4 不定义类对象使用类的静态成员变量
    	BB b1, b2, b3 ;
    	b1.printC() ;	 // 10
    	b2.AddC();		 // 11
    	b1.printC() ;	 // 11
    
    	//静态成员函数的调用方法
    	b3.getC() ;		// 用对象.
    
    	BB::getC() ;	//3 静态成员变量可以使用类的作用域来调用	BB::getC() ;
    
    
    }


    展开全文
  • Java对象与内存管理

    2020-12-22 21:33:07
     由于同一个JVM内每个累只对应一个Class对象,因此同一个JVM内一个类类变量只需一块内存空间。  对于实例变量而言,该类没创建一次实例,需要为实例变量分配一块内存空间,所以,程序中有几个实例,实例...
  • spring在设计的时候就是设计成对象的容器,spring负责管理项目中的所有对象,看成对象的管家。正是因为spring框架性质是属于容器性质的。spring支持ioc,aop,springjdbc,aop事务,junit测试等。下面分四部分讲述第...
  • 几乎所有windows内核组件,包括进程,线程,文件,设备等都属于内核对象,它们有一组共有数据以及几个函数指针以提供抽象访问,基本上c语言要玩“面向对象花都是这么干(linux内核里也有类似东西)。...
  • 实例变量和类变量内存分配 类变量 使用 static 修饰成员变量是类变量属于该类本身 实例变量没有使用 static 修饰成员变量是实例变量属于该类实例 由于同一个 JVM 内每个累只对应一个 Class 对象因此同一个 ...
  •  如果严格地遵循这些做法,可以消除绝大部分资源管理的问题。  然而,要想更灵活使用对象资源,仅仅这些还不够。譬如,若你想自己控制对象资源生命周期(不要在作用域结束时候自动被析构掉),那就应当...
  • 对象与内存管理

    2014-02-19 17:33:10
    1.实例变量和类变量内存分配  类变量 :使用static修饰成员变量是类变量,属于该类本身 ... 由于同一个JVM内每个类只对应一个Class对象,因此同一个JVM内一个类类变量只需一块内存空间。
  • 前言:在实际环境中,会有很多开发人员正在对一个项目编写SQL脚本,此时需要对对象的命名经行强制限定。完成这个任务可以使用强制的一些策略来实现。合理的命名对象并不仅仅是处于维护需要,有时候也能影响性能,...
  • 三、管理方案对象

    2018-08-29 09:40:00
    1. 方案描述:属于一个用户下,所有数据库对象的总体,表、视图、序列、索引、同义词、存储过程、存储函数、触发器、包和包体等方案与用户关系:一个用户就是一个方案,创建用户的时候,系统会自动创建一个同名的...
  • 物料需求计划(简称为MRP)与主生产计划一样属于ERP计划管理体系,它主要解决企业生产中物料需求与供给之间关系,即无论是对独立需求物料,还是相关需求物料,物料需求计划都要解决“需求什么?现有什么?还...
  • 实验四 数据库模式对象管理

    千次阅读 2016-12-31 20:26:55
    一、实验目的 1) 掌握表、索引、索引化表概念及管理 2) 掌握分区概念及分区管理 ...表空间、用户、角色、目录、概要文件及上下文问等数据库对象属于某个模式吗? 3) 如何进行模式选择与切换
  • Websphere mq对象之队列管理

    千次阅读 2014-01-22 22:03:28
    在WebSphere MQ中队列管理器是基本的软件系统,队列管理器可看成是队列和其他对象的容器。WebSphere MQ中的每一个队列都属于一个队列管理器,队列管理器是为应用程序和WebSphere MQ部件(一些管理工具)提供对队列...
  • 在学习Python过程中,经常会用到with语句,比如在进行文件操作时...其实并不是,这里涉及到上下文管理器方面知识,先来解释一下什么是上下文管理对象吧。 我们都知道,Python属于鸭子类型语言,只要一个类实...
  • Windows中的对象

    2020-11-03 09:28:31
    Windows核心编程写道识别内核对象的方法是看有没有SECURITY_DESCRIPTOR参数。内核对象是不属于进程的,是属于Windows内核的。进程只有一个内核对象句柄表,用来存放所有的内核对象句柄。所以,多个进程可以同时使用...
  • 小菜入行快两年了,记录一下自己工作过程中属于自己东西.同时也希望各位大虾指导给给予宝贵意见,如果能对各位同行朋友有所启发或帮助,实是荣幸之致。 这里先放一个对象缓存器,一般我们有些对象会经常使用,但是...
  • Java对象的生命周期

    2020-12-22 21:19:57
    要理解java对象的生命周期,我们需要要明白两个问题,  1、java是怎么分配内存的 ,2、java是怎么回收内存的。  喜欢java的人,往往因为它的内存自动管理机制,不喜欢java的人,往往也是因为它的内存自动管理...
  • 属于一个用户下,所有数据库对象的总称表、视图、序列、索引、同义词 存储过程、存储函数、触发器、包和包体一个用户就是一个方案,创建用户的时候,系统会自动创建一个同名的方案 二、常用的数据库对象 1、...
  • 一些关于面向对象的思考

    千次阅读 2016-03-02 00:06:22
    Python是一门神奇的语言,看了看里面一些关于面向对象的描述。觉得挺受启发的,在C++中同样适用。主要观点出自Magnus Lie Hetland的《Python基础教程》。1 将属于一类的对象放在一起。如果一个函数操纵一个全局变量...
  • 常说的GC(Garbage Collector) roots,特指的是垃圾收集器(Garbage Collector)的对象,GC会收集那些不是GC roots且没有被GC roots引用的对象。 一个对象可以属于多个root,GC root有几下种: Clas..

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 1,980
精华内容 792
关键字:

属于管理对象的是