自己动手写Python虚拟机-笔记

举报
大象比字长 发表于 2020/09/07 17:38:31 2020/09/07
【摘要】 自己动手写Python虚拟机 Day1-语言虚拟机的基本结构 今天主要分享的时预言虚拟机的基本结构,包括源代码的执行过程、编译器、字节码、栈帧、静态分配、动态分配、解释执行、JIT、类加载器、泛型等基础知识。下面介绍3种关键流程: java源代码执行过程 .java 通过 javac编译成 .class字节码文件,通过jvm加载...

             自己动手写Python虚拟机   Day1-语言虚拟机的基本结构


    今天主要分享的时预言虚拟机的基本结构,包括源代码的执行过程、编译器、字节码、栈帧、静态分配、动态分配、

解释执行、JIT、类加载器、泛型等基础知识。


image.png

下面介绍3种关键流程:

        java源代码执行过程

        .java 通过 javac编译成 .class字节码文件,通过jvm加载到内存中,解释器执行,函数调用多,则翻译成机器码执行


         java源码级编译器 

          javac  源代码--词法分析器--token流--语法分析器--语法树--语义分析器--注解抽象语法树--字节码生成器--jvm字节码


        java虚拟机执行引擎 

        JTI编译器 机器无关优化--中间代码--机器相关优化--中间代码--寄存器分配器--中间代码--目标代码生成器--目标代码


       一般我们看到的源码都已经被编译成class字节码文件了,所以我们一般都要通过反编译才能正常阅读,而JDK本身自带了一种javap,可以看到 字节码,常量池,方法定义等,大多数 IDE也带了反编译,可以反编译成.java文件,单其中 语法糖这些还原成基本语法,而有些变量名丢失,会被写成var1 var2等名称,我们阅读的时候,不能以为这样的名字也行,实际写代码,变量名还是要见名知意。


      javac编译代码的时候,会做泛型擦除,设定成泛型的上限。泛型是java多态重要的一部分,java 泛型类型使用Object类型代替,不支持基本类型,类型擦除机制、类型限定:上线 下线   编译期间做类型检查。

    .java文件会被编译成.class字节码文件,下面是字节码示例:

        iload_0 加载0  至栈顶

        iconst_1 将1压入栈顶 

        ifne 6 判断 不满足调制第6行

        ireturn  返回栈顶元素   

        isub  1 2出栈相减在放入栈中 

        iadd 1 2出栈相加在放入栈中   


  异常字节码:

    异常:Exception table 

    form  to  target  type

     0    3   6       Class

   第0行到第三行 发生异常跳到第6行,然后与type比较

  finally:生成的字节码 每个分支底下都会生成finally的字节码,这样,不管什么清空,finally的代码都会被执行。


 字节码中提到的栈帧其实指的是栈帧中的操作数栈,用来临时保存计算结果,栈帧中还有保存局部变量的栈帧。

 解释执行:基于栈的指令集 -- 基于栈的执行过程--解释器hotSpot,运行中翻译成机器码

 JIT:被多次调用,直接翻译成机器码。

 java7之后是分层编译 五层


静态分派 重载: 编译器确定

动态分派 重写:运行时确定


华为云账号:hw07606522

微信昵称:salad

微信账号:sela001

     

                          自己动手写Python虚拟机   Day2-构建对象系统


     今天主要分享的虚拟机运行时的对象结构,主要包括:Klass、oop、 Object header、Klass Point、动态类型、弱类型、虚拟机内存结构、类型等相关概念逻辑。

    image.png

      Klass 虚拟机中的类型,代表一类对象,类似于 java做反射等操作时会用到,Klass定义了不同类型的不同操作,例如:Integer 和 String 的+,对应的操作完全不一样。

      OOP 普通对象指针。

      每个普通对象对应到对应到虚拟机中,都有一个OOP,并且指向Klass。

      虚拟机运行时,内存布局跟Klass和OOP息息相关。

      image.png

     对于编程语言来说,分强类型语言和弱类型语言,类似于JS就是弱类型语言,不存在类型概念,对虚机机来说,运行的时候还有动态类型这个概念,动态类型指的是 变量声明时不指定类型,可以使用任意的值为该变量赋值,对象可以在运行时增加或者删除成员变量,对我们来说,python就是典型的动态类型。

    动态类型相对而言比较灵活,但是在实际运行中,效率时比较低的, Python在存储对象的属性时,采用的时哈希表来存储,每次都要通过对象的属性名从哈希表中查找,所以性能比较低,而V8采用的二级指针扩展,通过基址+偏移量来操作,避免了查表,所以性能比较高。

     虚拟机类型对象,兼备类型和对象的功能,既可以被打印,也能用来创建普通对象,是虚拟机实现反射功能的基础。

image.png


      对象关联着Klass,Klass又关联着对象,也就是说,我们需要对象的时候可以使用对象,我们需要类型的时候,可以使用Klass,这就是类型对象。


                     自己动手写Python虚拟机   Day3-内存管理


     今天主要分享的虚拟机运行时的内存管理,主要包括:垃圾回收基本概念、内存分配、垃圾识别、内存回收等基本概念、free-list算法、Bump-pointer算法、Tracing、Sweep算法、Compaction算法、Evacuation算法等。

      image.png

      整个内存管理其实都是围绕着垃圾回收进行的,如果内存管理不当,垃圾回收存在问题,那么程序就面临着内存溢出的风险,不能稳定的运行下去,所以内存管理也是一个虚拟机的一个核心,重要的组成部分。而我们熟悉并且了解虚拟机的垃圾回收机制,会使我们的程序更加健壮,避免内存溢出,让程序能够稳定进行下去。

     编程语言发展到现在,大部分语言已经从传统的手动内存管理,转成自动内存管理,自动管理的好处有以下几点:

            避免开发者的失误,导致内存管理错误

            责任分离,使开发者更专注于业务

            提高性能,更好的利用多核硬件资源

   

     虚拟机内存管理运行的第一步其实就是内存分配,给运行产生的对象分配内存,本次介绍了三种算法:

          Free-list: 空闲列表,把空闲内存记录在列表中,一般按对象大小分配

          Bump-pointer: 连续分配,从空闲空间开头开始连续分配

          Region-based: 基于区块分配器

     image.png

     给新对象分配内存,也就意味着有来的对象占用内存没有释放,所以接下来,我们要识别垃圾,识别垃圾使用Tracing, 从根开始寻找,如果一个对象不可从根抵达,就说明这个对象是垃圾,根一般指的是访问的变量,堆外到堆内的引用。

    找到垃圾对象之后,我们就要清理垃圾,释放内存,一般根据分配内存算法采用不同的垃圾回收算法:、

          Free-list:采用Sweep算法,将垃圾空间直接加回到空闲列表

          Bump-pointer: 有两种,Compaction压缩 算法,将活动性压缩到一侧

                                                Evacuation撤离 算法,将活对象复制到另一处,原空间全部释放

         image.png


下来我们看下比较成熟的java虚拟机,将内存直接分为不同区域,不同区域采用不同的垃圾回收策略。

image.png


     自己动手写Python虚拟机   Day4-函数与方法


     今天主要分享的从虚拟机的视角看方法函数,主要包括:高阶函数的概念、高阶函数的实现、变量、比较、闭包 、lambda、方法、decorator。

image.png

       首先介绍了函数,从虚拟机的角度来看函数,几种关于函数的概念:

             函数是第一公民:函数可以是参数,函数可以是返回值,那么函数就是这种编程语言的第一公民。

             高阶函数:函数可以接受其他函数为参数,那么这个函数为高阶函数。

      重点:尽量避免使用可变对象作为函数默认值。因为使用可变对象作为函数默认值,可变对象会跟函数绑定,这样导致函数多次被调用,可变参数持续再变化。

       对于高阶函数而言,不同的编程语言有不同的实现,主要分为两种:

             语言类库:通过编译器实现,以java为例,通过Scala实现。

             虚拟机:python就是通过虚拟机实现的

       还有一个重要的组成部分就是  变量,变量按作用域来说分为四种, local 局部  Enclosing 上下文   Global全局变量   Builtion内建变量,查找顺序是LEGB,作用域局部>外层作用域>当前模块中的全局>python内置作用域,变量作用域是可以改变的。闭包:就是能够读取其他函数内部变量的函数。例如在javascript中,只有函数内部的子函数才能读取局部变量,所以闭包可以理解成“定义在一个函数内部的函数“,python 可以通过nonlocal改变变量作用域来实现。

      关于对象的比较,python通过 is 来判断是否是一个对象,通过==判断值相等,而java则是通过==来判断是否是一个对象,equals来判断值相等。其中虚拟机都作用一些优化,类似于小数值对象(-128 到127 之间 一个byte),其实都是一个对象。

     lambda表达式,函数式编程,是目前来说比较新的一直方式,来源于 lambda演算。表达式:lambda<变量>:<表达式>

     对方法来说,与函数的区别,就是没有绑定owner。


 自己动手写Python虚拟机   Day5-高级主题


     今天主要分享的从高级主题,主要包括:迭代器、线程、协程、JIT、Profiling 、退优化。

       image.png       首先讲了迭代器,迭代器是一种设计模式,基本上每种编程语言种都有,并且都已经内置了,

            先看下迭代器特殊的几个字节码:

            GET_ITER 放到栈顶

            FOR_ITER 调用

            STORE_FAST 返回值

            SETUP_LOOP 跳出循环到的行数

            yield:pyhton种可以通过 yield将函数编程迭代器对象,主要是在函数运行到yield时,栈帧不销毁,存储起来。


        原来只接触过进程、线程的概念,没有接触过协程这个概念,今天通过这里,了解到了协程,协程可以在程序运行过程中中断,去执行其他内容,执行完了之后再回来执行,协程不是通过线程切换做这些的,而是通过程序控制,避免了线程切换的开销,提高了性能,由于不是多个线程,所以也避免了并发问题,不需要通过加锁来解决变量冲突。

      后面对第一章提到的概念,JIT  Profiling  退优化做了详细的讲解,原来只知道JIT是把热点函数翻译成机器码,通过学习知道了更深入的东西,首先需要申请一块具备 写权限并且有执行权限的内存,然后将热点函数翻译成机器码,将翻译好的机器码写入我们申请到的内存,当我们需要调用热点函数的时候,就直接调用这块内存,前面我们JIT中提到一个词。热点函数,什么函数叫热点函数呢?这个时候就用到Profiling,它是基于统计对程序的行为特征进行分析,根据调用次数定位出热点函数,JIT就是根据这个热点函数做机器码翻译,当我们已经翻译成机器码的函数假设不成立时,这时候就用到退优化了,解释器可以实时进行翻译,满足我们程序的正常运行,这一整套方案,既满足了效率,有满足了程序的逻辑。


【版权声明】本文为华为云社区用户原创内容,转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息, 否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。