php判断语句-PHP 是最适合在一篇文章中阅读的语言吗?

2023-08-26 0 2,650 百度已收录

飞跃 PHP 7

2015年12月3日PHP 7问世,这是PHP的一次飞跃。 PHP7修复了大量BUGphp判断语句,并增加了功能和句子糖分。 这些变化涉及到核心包、GD库、PDO、ZIP、ZLIB等熟悉和陌生的核心功能和扩展包。 PHP 7 删除了早已弃用的函数。 例如,mysql_系列函数在PHP 5.5中已弃用,并在PHP 7中删除。PHP 7的性能比HHVM低。 是 PHP 5.6 的两倍。

PHP从过去到现在仍然是单继承语言,不能同时继承两个子类的属性和方法。 为了解决这个问题,PHP有了Trait特性

PHP 在过去几年中取得了长足的进步。 成长为处理网络的卓越语言并非易事。

php国外的发展:

最初,php在新浪和百度广泛使用,并且有大量的php4.0代码。 当时,新浪和百度作为主要厂商,对整个php的需求量很大; 百度慧心辰和金山张彦推荐了nginx和lnmp环境为代表的个人推动了php在国外的快速建立和普及。 一时间,惠新辰、张岩等人成为了广大phper崇拜的对象。 惠新晨贡献的yaf框架一度成为百度内部的标准php框架。 yaf框架全称为php框架,实际上是php c语言的扩展。 后来,百度使用了hhvm,它将PHP代码转换为高级字符字节码(一种中间语言),运行时(JIT)编译器会将这种字节码翻译为机器码。 待定)。

(来自百度百科)

PHP在开源世界里更是精彩,phpcms、dedecms、帝国cms、discuz峰会(腾讯中标)、phpwind(阿里中标),几乎大大小小的网站都是用这个开源cmd搭建的,并且配备了非常好的模板可以自由更换,网店商城系统还有ecshop和ncshop等,这使得建站变得非常简单。 个人站长好一阵子了。 到目前为止,这款cms已经是搭建基础网站最简单、最有效的方式了,功能强大,使用灵活。

然而,随着互联网竞争越来越同质化和激烈,百度的关键词排名算法有更多的人工干预,个人站长逐渐淡出互联网,影响力不再那么广泛。 原来的bbs和个人网站逐渐退出,php的应用范围再次缩小。 随着大数据和云计算的出现,java的应用越来越多。

当java开始大面积发展的时候,php并没有停止。 大家都知道,PHP 7 发布时,速度是 PHP 5.6 的两倍。 PHP 7.3 的性能怎么样? Phoronix 在 PHP 7.3 Alpha1 发布时进行了基准测试。 事实证明,在常用的 PHPBench 基准测试中,PHP 7.3.0 Alpha 1 比当前稳定版本的 PHP 7.2 快约 7%,比 PHP 7.0 快 22%:

受慧心辰yaf框架的启发,国内其他c扩展框架也逐渐遍地开花,其中swoole是根据互联网最新的应用场景开发出新的解决方案,来自官方的说法:

使 PHP 开发人员可以编写高性能的异步并发 TCP、UDP、Unix Socket、HTTP,WebSocket 服务。Swoole 可以广泛应用于互联网、移动通信、企业软件、云计算、网络游戏、物联网(IOT)、车联网、智能家居等领域。使用 PHP + Swoole 作为网络通信框架,可以使企业 IT 研发团队的效率大大提升,更加专注于开发创新产品

swoole在更大程度上以c扩展的方式解决了php在短链接方面的弱点,让php也能拥有类似java的相关生态;

在swoole的基础上,还有easyswoole,它是基于swoole的扩展框架包。 目前已经比较成熟,但由于社区团队(个人)维护,毕竟精力有限,仔细研究后可以考虑。

Composer这种类似java生态的包管理工具也出现在php中。 ci、yii2、laravel 和 thinkphp 框架在开源世界中处于领先地位。 在中国或者二线城市,thinkphp已经成为标准配置,更符合国人的使用习惯。 文档特别全面,larave 在编程上变得越来越精彩和漂亮,yii2 提供的网页创建小部件也不错。

02 php zend vm 和 java jvm

语言的本质是编译解释为字节码(有的用解释,有的用编译,编译型语言驻留在显存中),最后由虚拟机编译成机器码,放置在显存中,由cpu执行; java有jvm,PHP也有zend vm,用于执行PHP的中间代码混合操作码。 PHP是一种解释性语言,即转换为机器代码并执行。 如果代码没有改变,机器码就不会被重写编译,也称为php opcode缓存,所以假设代码没有改变,php和编译型语言的差别不会很大。 更大的性能差异来自于执行各自中间代码的效率,因此php编译成了c++ hhvm来解决这个问题

字节码机器码转换关系

在讲其他语言之前,我们先来了解一下php虚拟机的Zend引擎,它也相当于java的jvm,这样可以更直观的了解到相关语言的本质都是类似的

PHP:一种解释性语言

PHP 通常被定义为“脚本语言”或“解释语言”。 什么是“解释性语言”?

所谓“解释型语言”是指用这些语言编写的程序不会直接编译成本地机器语言(native machine language),而是会编译成中间方法(代码)。 显然,这些中间方法是不可能的。 直接在CPU上执行(因为CPU只能执行本机机器指令),但是这些中间方式可以在使用本机机器指令编写的软件上执行(现在主要是用C语言编写)。

wiki中对虚拟机的定义是:虚拟机(Virtual Machine),在计算机科学的体系结构中,是指能够在计算机平台和最终用户之间创建一个环境的特殊软件,终端用户基于该软件进行操作关于软件创建的环境。 在计算机科学中,虚拟机是计算机的软件实现,可以像真实机器一样运行程序。

虚拟机是一台具体的计算机,具有自己的指令集和内存管理系统。 在这个虚拟机上实现的语言比低级语言更发明,也更容易学习。

PHP文件是如何解析的,生成的中间代码代表什么,生成的中间代码与实际的PHP代码如何对应,生成的中间代码如何执行? 执行过程中会传递哪些中间数据? 能否对整个虚拟机进行优化? 如何优化?

Zend虚拟机架构

具体Zend虚拟机的实现从概念层来说,我们可以将Zend虚拟机的架构分为:解释层、执行引擎、中间数据层。

Zend虚拟机架构图

当一段PHP代码进入Zend虚拟机时,会分两步进行:编译和执行。 对于解释型语言来说,这是一个创造性的举动,但是,目前的实现尚未完成。 现在,当PHP代码进入Zend虚拟机时,似乎是在这两步中执行的,但这两步对于常规执行过程来说是连续的,这意味着它不会转化为Java之类的东西。 编译语言也是一样:生成一个中间文件来存储编译结果。 如果每次都执行这样的操作,对于PHP脚本的性能来说将会是很大的损失。 虽然现在有APC、eAccelerator等缓存解决方案,但其本质没有改变,两个步骤不能分开单独开发。

解释层

解释层是 Zend 虚拟机执行编译过程的地方。 它包括词法分析、语法分析和编译生成中间代码三部分。 词法分析就是将我们要执行的PHP源文件进行划分,去掉空格,去掉注释,一一划分为token,对程序的层次结构进行处理。

语法分析是对接受的token序列按照定义的语法规则执行一些动作。 Zend虚拟机目前使用的Bison使用Backus-Naur Form(BNF)来描述句型。 中间代码的编译生成就是根据句型分析的结果生成中间代码,并与Zend虚拟机提出的操作码进行比较。 在 PHP5.3.1 中,Zend 虚拟机支持 135 条指令(参见 Zend/zend_vm_opcodes.h 文件),无论是简单的输出一句还是程序复杂的递归调用,Zend 虚拟机最终都会将所有的 PHP 代码进行转换我们编译成135条指令的序列,然后在执行引擎中按顺序执行。

中间数据层

Zend虚拟机执行PHP代码时,需要显存来存储很多东西,比如中间代码、PHP自带的函数列表、用户定义的函数列表、PHP自带的类、用户定义的类、常量、程序创建的对象、传递给函数或方法的参数、返回值、局部变量以及一些操作的中间结果等。我们把这个存储所有数据的地方称为中间数据层。

如果PHP以mod扩展的形式屈服于Apache2服务器,中间数据层的一些数据可能会被多个线程共享,比如PHP自带的函数列表。 如果只考虑单个进程的形式,当一个进程被创建时,它也会加载PHP自带的各种函数列表、类列表、常量列表等。 解释层编译PHP代码后,会在前面的列表中添加各种用户定义的函数、类或常量,但这个函数自身结构中各个数组的形参是不同的。

当执行引擎执行生成的中间代码时,会在Zend虚拟机的栈中添加一个新的执行中间数据结构(zend_execute_data),其中包括当前执行进程的活动符号列表的快照、一些局部变量、 ETC。

执行引擎

Zend虚拟机的执行引擎是一个非常简单的实现。 它只是根据中间代码序列(EX(opline))一步步调用相应的方法。 在执行引擎中,没有类似PC寄存器的变量来存储下一条指令。 Zend虚拟机执行某条指令时,当它的所有任务都执行完毕后,这条指令会自己调用下一条指令,即将序列的指针向前移动一个位置,从而执行下一条指令,最后执行返回语句等等。 这本质上是一个嵌套函数调用。

回到一开始的问题,PHP文件会经过词法分析、语法分析、中间代码生成三个步骤,解析为PHP的中间代码操作码。 生成的中间代码和实际的 PHP 代码之间并不存在完整的一一对应关系。 它只是根据用户给出的PHP代码、PHP语法规则和一些内部约定生成中间代码,并且这个中间代码还需要借助一些全局变量来传递数据和关联。 至于生成的中间代码的执行过程,是根据中间代码的流畅程度逐步执行的,并且依赖于执行过程中的全局变量。 当然,遇到一些函数跳转时,也会出现偏转,但最终还是会回到偏转点。

1.从物理机开始

虚拟机也是计算机,设计思想与物理机有很多相似之处;

1.1 冯诺依曼架构

冯·诺依曼是当之无愧的数字计算机之父。 当前的计算机采用冯·诺依曼架构; 设计思路主要包括以下几个方面:

1.2 汇编语言简介

任何架构的计算机都会提供一组指令;

一条指令由操作码和操作数组成; 操作码是操作的类型,操作数可以是立即数或存储地址; 每条指令可以有 0、1 或 2 个操作数;

指令是一串二进制; 汇编语言是二进制指令的文本形式;

push   %ebxmov    %eax, [%esp+8]mov    %ebx, [%esp+12]add    %eax, %ebxpop    %ebx

Push、mov、add、pop 等都是操作码;

%ebx寄存器; [%esp+12]内存地址;

操作数只是一个用于访问数据的存储区域; 操作数本身没有数据类型,其数据类型由操作码决定;

例如,movb 传输字节,movw 传输字,movl 传输双字等。

1.3 函数调用栈

过程(函数)是对代码的封装,只向外界暴露一组指定的参数和一个可选的返回值; 该函数可以在程序的不同地方调用; 假设过程P调用过程Q,Q执行完P后返回到过程; 为了实现这个功能,需要考虑三点:

大多数语言过程调用都使用堆栈数据结构提供的内存管理机制; 如下图所示:

函数的调用和返回对应一系列的push和pop操作;

函数执行时,会有自己的私有栈帧,局部变量分配在函数的私有栈帧上;

通常遇到的栈溢出是由于调用函数太深,不断压入栈造成的;

2.PHP虚拟机

虚拟机也是一台计算机。 参考物理机的设计,设计虚拟机时首先要考虑三个要素:指令、数据存储、函数栈帧;

下面从这三点来详细分析PHP虚拟机的设计思想;

2.1 手指

2.1.1 指令类型

任何架构的计算机都需要向外界提供一组指令集,它代表了计算机支持的一组操作类型;

PHP虚拟机提供了186种指令,这些指令定义在zend_vm_opcodes.h文件中;

//加、减、乘、除等#define ZEND_ADD                               1#define ZEND_SUB                               2#define ZEND_MUL                               3#define ZEND_p                               4#define ZEND_MOD                               5#define ZEND_SL                                6#define ZEND_SR                                7#define ZEND_CONCAT                            8#define ZEND_BW_OR                             9#define ZEND_BW_AND                           10

2.1.2 命令

2.1.2.1 指令的表示

指令由操作码和操作数组成; 操作码表示该指令的操作类型,操作数表示操作数本身或操作数的地址;

PHP虚拟机定义指令格式为:操作码操作数1操作数2返回值; 它使用结构体 _zend_op 来表示一条指令:

struct _zend_op {    const void *handler;    //指针,指向当前指令的执行函数    znode_op op1;           //操作数1             znode_op op2;           //操作数2    znode_op result;        //返回值    uint32_t extended_value;//扩展    uint32_t lineno;        //行号    zend_uchar opcode;      //指令类型    zend_uchar op1_type;    //操作数1的类型(此类型并不代表字符串、数组等数据类型;其表示此操作数是常量,临时变量,编译变量等)    zend_uchar op2_type;    //操作数2的类型    zend_uchar result_type; //返回值的类型};

2.1.2.2 操作数的表示

从内部可以看出,操作数是用结构体znode_op来表示的,其定义如下:

Constant、var、num等都是uint32_t类型。 这如何表示操作数? (指针既不能代表地址,也不能代表所有数据类型);

事实上,操作数、常量等大多数情况下使用的相对地址表示方法,表示的是相对于执行栈帧首地址的偏移量;

另外,_znode_op结构体中还有一个zval *zv数组,它也可以代表一个操作数。 该数组是一个指向 zval 结构的指针。 PHP虚拟机支持的所有数据类型均由zval结构表示;

typedef union _znode_op {        uint32_t      constant;        uint32_t      var;        uint32_t      num;        uint32_t      opline_num;    #if ZEND_USE_ABS_JMP_ADDR        zend_op       *jmp_addr;    #else        uint32_t      jmp_offset;    #endif    #if ZEND_USE_ABS_CONST_ADDR        zval          *zv;    #endif} znode_op;

2.2 数据存储

PHP虚拟机支持多种数据类型:整数、浮点、字符串、数组、对象等; PHP虚拟机如何存储和表示多种数据类型?

2.1.2.2节强调结构体_znode_op代表一个操作数; 操作数可以是偏移量(计算得到地址,即zval结构体的首地址),也可以是zval指针; PHP虚拟机使用zval结构来表示和存储各种数据;

struct _zval_struct {    zend_value        value;            //存储实际的value值    union {        struct {                        //一些标志位            ZEND_ENDIAN_LOHI_4(                zend_uchar    type,         //重要;表示变量类型                zend_uchar    type_flags,                zend_uchar    const_flags,                zend_uchar    reserved)     /* call info for EX(This) */        } v;        uint32_t type_info;    } u1;
    union {                                 //其他有用信息        uint32_t     next;                 /* hash collision chain */        uint32_t     cache_slot;           /* literal cache slot */        uint32_t     lineno;               /* line number (for ast nodes) */        uint32_t     num_args;             /* arguments number for EX(This) */        uint32_t     fe_pos;               /* foreach position */        uint32_t     fe_iter_idx;          /* foreach iterator index */        uint32_t     access_flags;         /* class constant access flags */        uint32_t     property_guard;       /* single property guard *    } u2;};

zval.u1.type表示数据类型,zend_types.h文件定义了以下类型:

#define IS_UNDEF                    0#define IS_NULL                     1#define IS_FALSE                    2#define IS_TRUE                     3#define IS_LONG                     4#define IS_DOUBLE                   5#define IS_STRING                   6#define IS_ARRAY                    7#define IS_OBJECT                   8#define IS_RESOURCE                 9#define IS_REFERENCE                10…………

zend_value存储具体的数据内容,结构体定义如下:

_zend_value占用16字节显存; long 和 double 类型将直接存储在结构体中; 引用、字符串、数组和其他类型使用指针存储;

代码中根据zval.u1.type数组判断数据类型,从而确定操作_zend_value结构体的哪个数组;

可以看出,字符串用zend_string表示,数组用zend_array表示……

typedef union _zend_value {    zend_long         lval;                double            dval;                zend_refcounted  *counted;    zend_string      *str;    zend_array       *arr;    zend_object      *obj;    zend_resource    *res;    zend_reference   *ref;    zend_ast_ref     *ast;    zval             *zv;    void             *ptr;    zend_class_entry *ce;    zend_function    *func;    struct {        uint32_t w1;        uint32_t w2;    } ww;} zend_value;

下图是PHP7中的字符串结构图:

2.3 再说说命令

2.1.2.1强调指令是用结构体_zend_op表示的; 最重要的两个属性是:操作函数、操作数(两个操作数和一个返回值);

操作数的类型(常量、临时变量等)不同,同一条指令对应的处理函数也会不同; 操作数类型在 Zend/zend_compile.h 文件中定义:

//常量#define IS_CONST    (1<<0)//临时变量,用于操作的中间结果;不能被其他指令对应的handler重复使用#define IS_TMP_VAR  (1<<1)//这个变量并不是PHP代码中声明的变量,常见的是返回的临时变量,比如$a=time(), 函数time返回值的类型就是IS_VAR,这种类型的变量是可以被其他指令对应的handler重复使用的#define IS_VAR      (1<<2)#define IS_UNUSED   (1<<3)  /* Unused variable *///编译变量;即PHP中声明的变量;#define IS_CV       (1<<4)  /* Compiled variable */

操作函数命名规则为:ZEND_[操作码]_SPEC_(操作数1类型)_(操作数2类型)_(返回值类型)_HANDLER

例如,赋值语句有以下多种运算功能:

ZEND_ASSIGN_SPEC_VAR_CONST_RETVAL_UNUSED_HANDLER,ZEND_ASSIGN_SPEC_VAR_TMP_RETVAL_UNUSED_HANDLER,ZEND_ASSIGN_SPEC_VAR_VAR_RETVAL_UNUSED_HANDLER,ZEND_ASSIGN_SPEC_VAR_CV_RETVAL_UNUSED_HANDLER,

对于$a=1,其运算函数为:

ZEND_ASSIGN_SPEC_CV_CONST_RETVAL_UNUSED_HANDLER; 该函数的实现如下:

static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_SPEC_CV_CONST_RETVAL_UNUSED_HANDLER(ZEND_OPCODE_HANDLER_ARGS){    USE_OPLINE    zval *value;    zval *variable_ptr;    SAVE_OPLINE();    //获取op2对应的值,也就是1    value = EX_CONSTANT(opline->op2);    //在execute_data中获取op1的位置,也就是$a(execute_data类似函数栈帧,后面详细分析)    variable_ptr = _get_zval_ptr_cv_undef_BP_VAR_W(execute_data, opline->op1.var);    //赋值    value = zend_assign_to_variable(variable_ptr, value, IS_CONST);    if (UNEXPECTED(0)) {        ZVAL_COPY(EX_VAR(opline->result.var), value);    }      ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();

2.4 函数栈帧

2.4.1 指令集

上面分析了指令的结构和表示,PHP虚拟机使用_zend_op_array来表示指令集:

struct _zend_op_array {    …………    //last表示指令总数;opcodes为存储指令的数组;    uint32_t last;    zend_op *opcodes;    //变量类型为IS_CV的个数    int last_var;    //变量类型为IS_VAR和IS_TEMP_VAR的个数    uint32_t T;    //存放IS_CV类型变量的数组    zend_string **vars;    …………    //静态变量    HashTable *static_variables;    //常量个数;常量数组    int last_literal;    zval *literals;    …};

注:last_var代表IS_CV类型变量的个数,存放在vars链表中; 整个编译过程中,每次遇到IS_CV类型的变量(类似于$something),都会遍历vars链表,检查是否已经存在,如果不存在,则插入到vars中,并将last_var的值设置为该变量的操作数; 如果存在,则使用之前分配的操作数

2.4.2 函数栈帧

PHP虚拟机实现了与1.3节物理机类似的函数栈帧结构;

使用_zend_vm_stack表示栈结构; 使用多个堆栈之间的prev数组生成双向数组; top和end分别指向栈底和栈顶,分别是zval类型的针;

truct _zend_vm_stack {    zval *top;    zval *end;    zend_vm_stack prev;};

考虑如何设计函数执行时的框架结构:当前函数执行时,需要存储该函数的编译指令,以及函数内部的局部变量等(2.1.2.2节强调操作数由结构体znode_op表示,其内部uint32_t表示操作数,表示当前zval变量相对于当前函数栈帧首地址的偏移量);

PHP虚拟机使用结构体_zend_execute_data来存储当前函数执行所需的数据;

struct _zend_execute_data {    //当前指令指令    const zend_op       *opline;     //当前函数执行栈帧    zend_execute_data   *call;     //函数返回数据              zval                *return_value;    zend_function       *func;                zval                 This;      /* this + call_info + num_args */    //调用当前函数的栈帧           zend_execute_data   *prev_execute_data;    //符号表    zend_array          *symbol_table;#if ZEND_EX_USE_RUN_TIME_CACHE    void               **run_time_cache;  #endif#if ZEND_EX_USE_LITERALS    //常量数组    zval                *literals;        #endif};

当函数开始执行时,需要将对应的函数栈帧分配给该函数并放入栈中。 代码如下:

static zend_always_inline zend_execute_data *zend_vm_stack_push_call_frame(uint32_t call_info, zend_function *func, uint32_t num_args, zend_class_entry *called_scope, zend_object *object){    //计算当前函数栈帧需要内存空间大小    uint32_t used_stack = zend_vm_calc_used_stack(num_args, func);    //根据栈帧大小分配空间,入栈    return zend_vm_stack_push_call_frame_ex(used_stack, call_info,        func, num_args, called_scope, object);}//计算函数栈帧大小static zend_always_inline uint32_t zend_vm_calc_used_stack(uint32_t num_args, zend_function *func){    //_zend_execute_data大小(80字节/16字节=5)+参数数目    uint32_t used_stack = ZEND_CALL_FRAME_SLOT + num_args;    if (EXPECTED(ZEND_USER_CODE(func->type))) {        //当前函数临时变量等数目        used_stack += func->op_array.last_var + func->op_array.T - MIN(func->op_array.num_args, num_args);
    }    //乘以16字节    return used_stack * sizeof(zval);}//入栈static zend_always_inline zend_execute_data *zend_vm_stack_push_call_frame_ex(uint32_t used_stack, uint32_t call_info, zend_function *func, uint32_t num_args, zend_class_entry *called_scope, zend_object *object){ //上一个函数栈帧地址    zend_execute_data *call = (zend_execute_data*)EG(vm_stack_top);    //移动函数调用栈top指针    EG(vm_stack_top) = (zval*)((char*)call + used_stack);    //初始化当前函数栈帧    zend_vm_init_call_frame(call, call_info, funcnum_argscalled_scopeobject);    //返回当前函数栈帧首地址    return call;}

从内部分析,可以得到函数栈帧结构图如下:

PHP虚拟机也是一台计算机。 我们需要重点关注三点:指令集(包括指令处理函数)、数据存储(zval)、函数栈帧;

此时,虚拟机可以接受指令并执行指令代码;

然而,PHP 虚拟机专用于执行 PHP 代码。 如何将PHP代码转换成PHP虚拟机可以识别的指令——编译;

PHP虚拟机还提供了编译器,可以将PHP代码转换成它可以识别的一组指令;

理论上,你可以定制任何语言,只要你实现一个编译器,可以将你自己的语言转换成PHP可以识别的指令代码,就可以被PHP虚拟机执行;

我们再看一下java虚拟机:

1.Java代码编译执行过程

1、源代码编译:通过Java源代码编译器将Java代码编译成JVM字节码(.class文件)

2.类加载:JVM的类加载是通过ClassLoader及其泛型完成的

3、类执行:字节码放入显存,进入JVM虚拟机,由类库解释执行

注:Java平台是由Java虚拟机和Java应用程序socket构建而成,Java语言是进入该平台的通道。

用Java语言编写和编译的程序可以在这个平台上运行

2.JVM简介

1、java程序经过一次编译后,java代码被编译成字节码,即class文件,然后借助不同的java虚拟机在不同的操作系统上进行解释,最后转换成不同的机器码平台,最终得到实现

2、Java虚拟机(JVM)处于核心,这是程序与底层操作系统和硬件无关的关键。

JVM下面是移植套接字,它由适配器和Java操作系统两部分组成,依赖于平台的部分称为适配器,JVM通过移植在特定平台和操作系统上实现插座

JVM之上是Java的基本通用解释器和扩展解释器及其API。 使用 Java API 编译的应用程序和小程序可以在任何 Java 平台上运行,而无需考虑底层平台。

Java虚拟机(JVM)实现了程序与操作系统的分离,从而实现了Java的跨平台

3、JVM在其生命周期中有一个明确的任务,就是运行Java程序。 因此,当Java程序启动时,就形成了JVM的一个实例; 当程序运行结束时,实例也会消失。 向上

4、三种JVM:①Sun公司的HotSpot ②BEA公司的JRockit ③IBM公司的J9 JVM

在JDK1.7及之前,我们使用的是Sun的HotSpot,但是由于Sun和BEA都是Oracle竞标的,所以jdk1.8会利用Sun的HotSpot和BEA的JRockit的精华来生成jdk1.8的JVM。

3.JVM架构

1.Class Loader类加载器

负责加载.class文件,class文件在文件开头有特定的文件标识符,ClassLoader负责加载class文件等。至于能否运行,则由Execution Engine决定。

① 找到并导出二进制类文件

② 验证导出类的正确性

③ 为类分配并初始化显存

④ 帮助解析符号引用。

2. Native Interface本地套接字

本地socket的作用就是将不同的编程语言集成到Java中。 它的初衷是集成C/C++程序。 在显存中开辟了一个特殊区域用于处理标记为

Native代码,其具体做法是在Native Method Stack中注册native方法,并在Execution Engine执行时加载native库。

目前这种方式用得越来越少,除了硬件相关的应用,比如通过Java程序驱动打印机,或者Java系统管理生产设备,在企业级应用中早已比较少见。

因为现在异构领域之间的通信已经非常发达了,比如可以使用Socket通信,也可以使用Web Service。

3.执行引擎执行引擎:执行以加载类的方式封装的指令,即方式。

4.Runtime data area 运行数据区域(即:下一节将介绍的虚拟机显存或JVM显存)

从整个计算机的显存中,开发出一块显存来存储Jvm需要使用的对象、变量等,分为:方法区、堆、虚拟机栈、程序计数器、本地模式栈。

四、JVM内存结构

1. 程序计数器PC寄存器

每个线程都有一个程序计算器,它是一个指针,指向模式区中的模式字节码(下一条要执行的指令代码),执行引擎读取下一条指令,这是一个很小的显存空间。 几乎可以忽略不计。

程序计数器(Program Counter Register)是一块很小的内存空间,它的作用可以看作是当前线程执行的字节码的行号指示器。 在虚拟机的概念模型中(这只是一个概念模型,各种虚拟机可能会通过一些更高效的方法来实现),字节码例程的工作原理是通过改变这个计数器的值来选择下一条要执行的字节码指令、分支、循环、跳转、异常处理、线程恢复等基本功能都需要依赖这个计数器来完成。 由于Java虚拟机的多线程是通过轮流切换线程和分配处理器执行时间来实现的,因此在任何给定时刻,一个处理器(对于多核处理器来说就是一个核)只会执行一条线程指令。 因此,为了线程切换后能够回到正确的执行位置,每个线程必须有一个独立的程序计数器。 线程之间的计数器互不影响,独立存储。 我们将这种类型的内存区域称为视频内存的“线程私有”。 如果线程正在执行Java方法,则该计数器记录正在执行的虚拟机字节码指令的地址; 如果线程正在执行 Natvie 方法,则计数器值为空(未定义)。 该视频内存区域是 Java 虚拟机规范中唯一未指定任何 OutOfMemoryError 条件的区域。

2. 原生方法栈

将本地方法注册到本地方法栈中,并在执行引擎执行时加载本地库

本地模式栈与虚拟机栈基本类似,不同的是虚拟机栈服务于虚拟机执行的Java模式,而本地模式栈服务于Native模式

3. 方法区

用于存储虚拟机加载:静态变量+常量+类信息+运行时常量池(类信息:类版本、字段、方法、接口、构造函数等描述信息)

默认最小值为 16MB,最大值为 64MB。 模式区域的大小可以通过 -XX:PermSize 和 -XX:MaxPermSize 参数来限制

对于习惯在HotSpot虚拟机上开发和部署程序的开发者来说,很多人愿意将方法区称为“永久代”(Permanent Generation)。 两者本质上并不等同,只是因为HotSpot虚拟机的设计团队选择将GC分代收集扩展到方法区,或者使用永久代来实现方法区。 对于其他虚拟机(如BEA JRockit、IBM J9等),没有永久代的概念。 甚至对于HotSpot虚拟机本身来说,根据官方的路线图信息,现在已经有计划放弃永久代,“搬”到Native Memory来实现方法区。 Java 虚拟机规范非常仔细地限制了这个区域。 除了不像Java堆那样需要连续显存并且可以选择固定大小或可扩展之外,还可以选择不实现垃圾回收。 相对而言,该区域的垃圾回收行为比较少见,但它并不像数据进入方法区时的永久代的名字那样“永久”存在。 这方面显存回收的目标主要是常量池的回收和类型的卸载。 总体来说,这方面的恢复“结果”是比较不理想的,尤其是类型卸载。 条件相当严格,但这部分区域回收确实有必要。 在Sun的BUG列表中,出现了几个严重的BUG,因为低版本的HotSpot虚拟机没有完全回收这个区域,导致内存泄漏。 According to the Java virtual machine specification, when the mode area fails to meet the memory allocation requirements, an OutOfMemoryError exception will be thrown.

4. Stack JVM Stack

All kinds of basic data types known to the compiler (boolean, byte, char, short, int, float, long, double), object references (reference pointers, not the object itself)

The stack is the memory model for java method execution:

When each method is executed, a "stack frame" is created to store local variable table (including parameters), operation stack, method exit and other information.

The process from the call to the execution of each method corresponds to the process of a stack frame from pushing to popping in the virtual machine stack.

(Local variable table: stores all kinds of basic data types known to the compiler (boolean, byte, char, short, int, float, long, double), object references (reference pointers, not the object itself),

The 64-bit long and double type data will occupy the space of 2 local variables, and the other data types will only occupy 1 space.

The video memory space required by the local variable table is allocated during compilation. When entering a method, how many local variables this method needs to allocate in the stack frame is completely determined, and the stack frame will not change the local variable table during operation. size space)

The life cycle of the stack follows the life cycle of the thread. It is created when the thread is created, and the stack memory is released when the thread ends, which is private to the thread.

5. Heap Java Heap

All object instances and linked lists must be allocated on the heap, and the only purpose of this memory area is to store object instances

The heap is the largest piece of video memory managed by the Java virtual machine. The Java heap is a memory area shared by all threads, created when the virtual machine starts

The heap is the most important area to understand the Java GC mechanism, no one

Structure: Cenozoic generation (Eden area + 2 Survivor areas) Old generation permanent generation (HotSpot has)

New generation: newly created objects --> Eden area

After GC, the surviving objects move from Survivor area 0 in Eden area to Survivor area 1

GC again, the surviving objects move from Survivor District 1 in Eden District to Survivor District 0

Old generation: If the object has survived long enough in the new generation without being cleared (that is, survived after several Young GCs), it will be copied to the old generation

If the newly created object is relatively large (such as a long string or a large linked list), and the space in the new generation is insufficient, the large object will be directly allocated to the old generation (large objects may trigger early GC, so it should be used less, and short-lived large objects should be avoided. object)

The space of the old generation is usually larger than that of the new generation, which can store more objects, and the number of GCs that occur in the old generation is less than that of the young generation

Permanent generation: It can be simply understood as a mode area (the two are not equivalent in essence)

As mentioned above: For developers who are accustomed to developing and deploying programs on the HotSpot virtual machine, many people are willing to call the mode area "permanent generation", and the two are not equivalent in essence.

Just because the design team of the HotSpot virtual machine chose to expand the GC generational collection to the method area, or use the permanent generation to implement the method area.

For other virtual machines (such as BEA JRockit, IBM J9, etc.), there is no concept of permanent generation

Even for the HotSpot virtual machine itself, according to the official roadmap information, there is now a plan to abandon the permanent generation and "move" to Native Memory to implement the method area

Jdk1.6 and before: the constant pool is allocated in the permanent generation

Jdk1.7: Yes, but it has already gradually "deleted the permanent generation"

Jdk1.8 and later: no permanent generation (java.lang.OutOfMemoryError: PermGen space, this error will not appear in JDK1.8)

6. Direct memory Direct Memor

Direct video memory is not the video memory managed by the JVM. It can be understood that the direct video memory is the video memory of the machine other than the JVM. For example, if you have 4G video memory and the JVM occupies 1G, the remaining 3G is direct video memory.

There is a channel (Channel) and buffer (Buffer) based video memory allocation method in JDK, which allocates the native function library implemented by C language in direct video memory, and uses DirectByteBuffer stored in the JVM heap to reference

Since the direct video memory is limited by the video memory of the machine, the exception of OutOfMemoryError may also occur.

总结:

We can detect:

1: Whether it is java or php, the virtual machine does the same job. It is compiled into intermediate code, responsible for the execution of intermediate code, and memory management during execution

2: The virtual machine itself is a system, which is responsible for the operating system of this language management, and leaves a socket for external docking

3: As for the different data structures of java and php, different intermediate codes, etc. are slightly different, but in the end they all live in the cpu to execute the machine code. The closer the intermediate code is to the machine code, the higher the execution efficiency

03 Comparison between php and java

1. Sentence comparison between php and Java

1), Notes

Java supports: double slashes (//), /**/; PHP supports: double slashes (//), # symbol, /**/.

2), case sensitive

In java, all function names, keywords, classes, variables, etc. are case-sensitive; in PHP, variables are case-sensitive, while user-defined functions, classes, and keywords are not case-sensitive.

PHP is a server scripting language for interpretation and execution. First of all, PHP has the characteristics of being simple and easy to use. The syntax is similar to C language, so programmers who have learned C language can quickly become familiar with PHP development.

To learn java, you need to learn java sentence patterns and familiarize yourself with some core generics. Java is object-oriented everywhere, and learning java understands object-oriented programming methods, so java is not as easy to learn as php.

More intuitive expression: php's sentence patterns and use are getting easier, and java uses more generics and packages to support the ecology

2. Comparison of php and Java system architecture design

If you have to talk about the system architecture, there is not much difference between php and java in essence. The main architecture is from domain name resolution load balancing to code server to cache and finally to the database. Clustering and asynchronous forms can solve architectural problems very well. Here we can see that the system architecture has a certain relationship with the language, but the main reason is not at the language level.

Some commonly used data php can go directly to the cache (memached and redis) through the nginx module, and call the code refresh after invalidation. Sometimes it does not need to go through the language layer. This is the charm of the architecture, but it falls into the details of java and php There are still some differences in language aspects, such as concurrency and long link processing.

实在要追求运行效率,php的c 扩展足以解决,所以php大并发构架不如java的说法是不完全正确的,java能支持的大并发构架,php也是可以实现的;架构好不好,和语言有一定关系,更多的是在于使用这门语言的人。

3、php与Java访问数据库速率的比较

php对于不同的数据库采用不同的数据库访问插口,所以数据库访问代码的通用性不强。例如:用Java开发的Web应用从MySQL数据库转入Oracle数据库只须要做极少的更改。而php则须要做大量的更改工作。

Java通过JDBC来访问数据库,通过不同的数据库厂商提供的数据库驱动便捷地访问数据库。访问数据库的插口比较统一。

4、php与Java源代码安全的对比

PHP开发的程序的源代码通常是公开的,也可以通过zend加密,至于安全性,通过sql注入拦截,外界传入数据拦截再加上linux服务器安全,自然也是安全的。

Java开发的程序,最后用户领到的是只是一些编译好的class类,安全性是有一定提升。

5、php与Java开发成本的对比

PHP最精典的组合就是:PHP + MySQL + nginx + linux 。非常适宜开发中小型的Web应用,开发的速率比较快。而且所有的软件都是开源免费的,可以降低投入。

Java的Web应用服务器有免费Tomcat、JBoss等,如果须要更好的商业化的服务有:Web Sphere和Web logic。

6、php与Java的性能比较

php7的性能早已有很大提升,某些情况下与java性能相当,但是因为java是编译型语言php判断语句,变量常驻显存以及多线程,java在性能上还是有不错的表现

7:我该学php还是java

php简单易学,容易上手,java生态丰富,在大数据上优势显著,php做到一定程度只在web范围内,不易于长远发展,所以做php的朋友可以做到一定程度同时开始java和go的学习

8:我们公司技术选型用java还是go

看公司目前的业务状态,如果是web,用java和go都一样,而在于你的技术leader对java还是go更熟悉,也在于你所在城市那个语言的市场供应比较多

9:为什么市场上的php越来越少

这是一个很值得思索的问题,至从微服务下来之后,spring boot,spring cloud把web开发抢占了很大一部分江山,而整个互联网步入更加深层次的存量竞争,原来简单的web系统早已不满足业务需求;同时更在于php和java两个语言从业人员的本身基础问题,因为php相对上手容易,所以市场上有大量基础通常的人存在,导致业务系统经常有一些状况,而java要能跑上去对基础还是有一定要求,结果就导致了似乎java系统越来越结实的表象,实质上语言上是差异不大,而是两种语言的从业人员的素养有一定差异。

php与其php与其他语言还有一些对比和差异,这里小编就不一一展开了,以后再详尽展开

结论思索

回到最开始的问题,php是不是世界上最好的语言,我们一起来揭露这个谜底,相信看完里面的文字心中应当有一定数了

1:从语言本身设计或则出发点来讲php要说是世界上最好的语言,可能并不会很夸张,只是其应用场景更多的局限与web

2:至于"php是世界上最好的语言"这个段子哪些时侯开始留传的,我也不能溯源到,只是想到一种可能场景:在一个团队内,有java也有php,突然某日java能解决的问题php没解决了,主管要把php转java,而php是不服的,大声抒发:php是世界上最好的语言

或许,php是世界上最好的语言是基于以上两个诱因同时而形成的,至于php是不是世界上的语言并不重要,各自有各自的场景,而"php是世界上最好的语言"这个段子就让他继续留传吧,这是程序猿界少有的段子,是我们happy的源泉,也或许是很多人骄傲的源泉,开心很重要

写一篇不错的文章须要花好多的时间,更重要的是那些愈发接近真相的东西我们须要让更多的人晓得,是对社会,是更多人共多的贡献,只有你们都有钱了,我们自己也能够让她们头上赚到更多的钱

觉得不错就帮小编点赞和转发吧。

肉眼品世界

发现世界之美好(ID:find_world_fine),独立理智之观点,从技术构架,到研制管理再到商业视觉再到创业人生的升级之路,深厚的技术、研发管理体系价值观与互联网商业剖析输出,助力每位个人与企业高速发展。愿景:通过共赢的形式推进社会知识创新,释放社会创新活力

收藏 (0) 打赏

感谢您的支持,我会继续努力的!

打开微信/支付宝扫一扫,即可进行扫码打赏哦,分享从这里开始,精彩与您同在
点赞 (0)

悟空资源网 php php判断语句-PHP 是最适合在一篇文章中阅读的语言吗? https://www.wkzy.net/game/157538.html

常见问题

相关文章

官方客服团队

为您解决烦忧 - 24小时在线 专业服务