我认识的python(1)

274 阅读6分钟

第一篇博客的由来

从接触编程的时候,我就经常会看一些博客内容。在接触了很多大牛的分享之后,有一个想法一直在脑海中盘旋。那就是拥有一个自己的博客,并开始写一些自己对技术的心得体会。终于今天我下决心开始写一些简单的博客内容,做自己的知识的沉淀。

解释型语言和编译型语言的区别

这个话题怎么说呢,对于编译型语言我其实不是很熟悉,虽然大学有初浅地学过一学期的c语言,但当时的我对技术还是处于迷茫的状态,所以那时候对编译型语言更是一头 雾水。

不过前段时间刚好接触一个项目,稍微看了点c++,结合自己的一些理解,如果有什么理解上的错误,麻烦大家见谅,如果能够指导更加感谢哈。
首先先说下编译型语言吧,我理解的编译型语言的执行过程是cpu通过加载经过编译汇编链接后的指令集,并依次执行。cpu通过取址,分析,执行等过程最终完成一条条指令。

那么我理解的解释型语言是怎么样,首先大多数解释型语言是运行在解释器上,解释器的责任其实雷同于cpu,只不过解释器加载的是字节码(中间语言格式)而cpu是加载真正的指令集,解释器执行的过程大致是通过读取每一行的字节码,通过分析字节码的指令,然后转化成对应的执行过程(像python的话,默认安装的解释器是cpython,他分析字节码后执行相应的c语言的代码)。当然解释器的形态其实可以理解成一个应用程序,它的执行其实对应就是编译型语言的执行过程,然后解释型语言则在依赖于它进行执行。

那么很明显,如果实现同等的功能,解释型语言的运行时间必然会比编译型语言长(单纯针对机器码指令的数量来说),这也是编译型针对解释型的优势点所在。所以一般针对cpu密集型应用,编译型语言的时间开销远小于解释型,但如果软件的场景是io密集型,由于执行集运行时间远小于io的读取写入时间,所以在这个场景下,两个是会有差距,但不会很明显。

但是编译型语言最终需要运行的时候会转化成二进制的机器码指令,这个指令的由来是源代码通过预处理-编译-汇编-链接最终变成真正的可执行文件。 这里添加一点自己的理解:

  1. 预处理这个过程其实是处理源代码内所有预处理代码。比如include指令,它的执行过程会将它用到的头文件内容替换到源文件include相应的位置,像其中涉及到的函数定义最终对应汇编语言是一个预留的代码段声明,而像define则会将代码中所有的宏定义转化成相应的代码内容。
  2. 编译过程是将预处理后的c语言代码转化成汇编代码,大概就是产生代码段,堆栈段,bss段,数据段等。
  3. 汇编过程就是将上一步的汇编代码转化成平台相关的机器码。
  4. 链接过程我简单的理解就是说,但你的代码里面用到很多模块的声明函数,但假如你没有把相应的代码拷贝到机器码相应位置时候,这个代码其实是运行不起来,这个时候就必须填充预留的代码段的实际内容,然后更改机器码中代码段的相对地址绝对地址的值,这样这份文件就可以真正的运行了。

所以这里可以看到一份代码假如要在不同环境下运行,由于指令集的不同,必须重新进行编译,没办法直接拷贝运行。而与之相反,解释型语言有一个优点就是它具备跨平台的能力,只要解释器的解析运行这一层保持不变,底层去适配不同的环境,那么在解释器上运行的语言其实与底层的系统环境是隔离的。解释型语言另外一个好处就是内存管理对用户是透明的,因为像编译型语言的话,如果它需要用到某些内存空间,需要进行malloc开辟内存空间,然后这个空间通过进程的虚拟空间映射到真实的物理页(段页式以及进程地址段PCB某个字段的数据结构管理),然后当内存使用完毕,需要进行free释放内存空间。而解释型语言是由解释器进行运行,所以可以猜测到解释运行过程中的内存是不确定的,假如每次需要内存都去进行申请,效率明显不高。这个时候预先分配一部分内存,然后对这部分内存进行管理。按大中小划分内存,然后根据对象使用频次进行内存管理的优化,对象的回收不释放而是重新标记等待分配(当然内存不够的时候会进行释放)。

像python语言,默认的垃圾回收是引用计数,在对象引用变化期间会调整pyobject结构体的引用字段,但字段为0则进行对象回收,当然回收只是回归到空闲链表上。引用计数是一个比较简单的垃圾回收算法,但它没办法解决循环引用,针对这个问题,python引入了标记删除回收算法,在一定回收周期内,对不可达的对象进行回收(有一条链表维护引用使用中的对象),这个方法可以解决循环引用,但对于部分不经常被回收的对象,频繁做标记回收显得影响性能,所以这个时候涉及一个分代管理的算法,本质上就是对不经常变动的对象延长回收时间,这样就可以减少每次垃圾回收扫描的时间。