链接:https://www.zhihu.com/question/20377398/answer/141328982
来源:知乎
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
https://github.com/zendtech/php-src/tree/zend-jit/ext/opcache/jit
https://www.phpclasses.org/blog/post/493-php-performance-evolution.html
![v2-97faa2d0b91b698ce70d7ca75aae4f28_b.png](https://i-blog.csdnimg.cn/blog_migrate/71e4ab19de9a2d537c911dd47c7a0dfb.png)
![](https://i-blog.csdnimg.cn/blog_migrate/c440a1dd8f2dd35da7587d565552d5d1.png)
PHP的库函数用C实现,而Java核心运行时类库(jdk/jre/lib/rt.jar,大于60MB)用Java编写(jdk/src.zip), 所以Java应用运行的时候,用户编写的代码以及引用的类库和框架都要在JVM上解释执行. Java的HotSpot机制,直到有方法被执行10000次(-XX:CompileThreshold=10000)才会触发JIT编译, 在此之前运行在解释模式下,以避免出现JIT编译花费的时间比方法解释执行消耗的时间还要多的情况.
PHP内置模板引擎,自身就是模板语言.而Java Web需要使用JSP容器如Tomcat或第三方模板引擎.
PHP内置HTTP服务器和SQLite数据库,以及Apache模块实现libphp.so和FastCGI服务PHP-FPM.而Java Web开发时普遍需要使用第三方的Servlet容器Tomcat等.PHP内置的单进程HTTP服务器(可用于快速开发和测试):
php -S 127.0.0.1:8080 -t /www
PHP-FPM跟Nginx一样,是多进程的架构,worker进程处理请求,master进程不处理请求,只负责维护worker进程,比如定量重启,崩溃重启等.PHP-FPM支持进程池的特性,不同进程池相互隔离,互不影响.比如你可以配置一个监听9000端口的进程池www和一个监听9001的进程池io来分离IO密集脚本:
nginx.conf: 访问io.php的请求都交给监听9001的PHP-FPM进程池处理
location = /io.php {
include fastcgi_params;
fastcgi_pass 127.0.0.1:9001;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
}
php-fpm: 正常脚本由静态www池处理,阻塞脚本由动态io池处理
[www]
;名为www的进程池监听9000端口,常驻进程数量为固定4个
listen = 127.0.0.1:9000
pm = static
pm.max_children = 4
[io]
;名为io的进程池监听9001端口,进程数常驻4个,最大8个
listen = 127.0.0.1:9001
pm = dynamic
pm.max_children = 8
pm.start_servers = 4
pm.min_spare_servers = 4
pm.max_spare_servers = 4
其中I/O密集这个进程池[io]采用动态的prefork进程,比如这里是繁忙时8个,空闲时4个.
利用PHP-FPM提供的池的隔离性,分离计算密集和I/O密集操作,可以减少阻塞对整个PHP应用的影响.
也就是说,PHP通过多进程利用多核实现并发,而Java普遍通过多线程实现并发,因为一个JVM实例就是一个进程.
另外,PHP也可以运行在多线程模式下,比如Apache的event MPM和Facebook的HHVM都是多线程架构.不管是多进程还是多线程的PHP Web运行模式,都不需要PHP开发者关心和控制,也就是说PHP开发者不需要写代码参与进程和线程的管理,这些都由PHP-FPM/HHVM/Apache实现.
PHP-FPM进程管理和并发实现并不需要PHP开发者关心,而Java多线程编程需要Java开发者编码参与.PHP一个worker进程崩溃,master进程会自动新建一个新的worker进程,并不会导致PHP服务崩溃.而Java多线程编程稍有不慎(比如没有捕获异常)就会导致JVM崩溃退出.
对于PHP-FPM和Apache MOD_PHP来说,服务进程常驻内存,但一次请求释放一次资源,这种内存释放非常彻底. PHP基于引用计数的GC甚至都还没发挥作用程序就已经结束了. 而且,在PHP脚本中用unset显式释放内存也是立竿见影的,不会有延时.而Java的内存回收严重依赖GC机制,高并发下的Full GC会导致Java服务雪崩:JVM忙于用GC回收内存无法处理请求,而新请求又源源不断地到来.
PHP的运行模式决定了PHP天然支持热部署,而Java要实现热部署并不容易.这也是为什么在虚拟主机托管环境里PHP占绝对优势的原因,因为开发者通过FTP上传PHP文件到虚拟空间就实现了代码更新和部署.
PHP跨进程共享数据,除了使用基于文件的session机制和鸟哥开发的无锁共享内存缓存扩展Yac.Linux上还可以使用内存文件系统(tmpfs)上的SQLite(如/dev/shm/data.sqlite3).而Java程序的生命周期随JVM常驻内存,线程可以访问共享数据.
PHP不存在数据库访问速度比Java慢的问题.PHP的数据库驱动如mysqlnd等都是C实现的驱动,而Java的数据库驱动JDBC是Java实现的驱动,PHP的驱动性能并不吃亏.而且PHP同样支持数据库持久连接,也就是多个请求能复用一个数据库连接,并不需要每个请求都打开一个数据库连接.比如下图就是两个PHP-FPM工作进程跟MySQL保持的两个长连接:
![v2-fafd0f1b884a42b8e43d631a05b49319_b.png](https://i-blog.csdnimg.cn/blog_migrate/87aca885574055c4abdcd3afd897d193.png)
![](https://i-blog.csdnimg.cn/blog_migrate/64c54f764bb8d5282e526f9c75144d9b.png)
PHP跟Java都诞生于1995年,没有PHP相对Java是后起之秀的说法,反倒是PHP一开始就是用于Web开发,而Java不是.Java的前身Oak语言,是为了嵌入式软件开发而设计.
C实现的PHP后来吸收了C++的对象编程思想,加入了对象编程支持.既可以用过程式,也可以用对象,更灵活.而Java必须完全面向对象编程,甚至还要把类名和文件名挂钩.
PHP能不能开发大型应用,取决于使用者是否因地制宜地使用PHP. 比如,PHP就不适合用来开发数据库引擎(大多都是C/C++实现)等计算密集型应用.Java在计算密集型应用上相比PHP更有优势,比如HBase数据库使用了Java实现.不过大多数Web应用都是I/O密集型应用,这里面包括网络I/O,文件系统I/O,数据库I/O.
PHP是C实现的Web快速开发框架,不依赖第三方框架也能实现快速开发.而Java Web开发普遍依赖Spring等第三方框架.
补充:
回复
数据库持久连接很容易用,mysqli里host参数传递p:127.0.0.1就能开启持久连接,pdo_mysql里把PDO::ATTR_PERSISTENT设为true也可以开启持久连接.而且PHP的数据库持久连接也不依赖PHP-FPM,我的截图不过是举例说明,其实用Apache也一样可以.只是PHP-FPM的进程数或者Apache进程/线程数最好配置为固定数量,而且要求数量不能超过MySQL最大连接数(max_connections默认是151).
进程间共享数据,除了Yac,我不是还说了Linux内存文件系统tmpfs上的SQLite么?SQLite的锁机制够用了呀,连事务都支持,你担心什么?而且内存上也不需要担心SQLite的读写性能限制,完全是SQLite引擎在内存上的计算密集操作.能利用Linux的tmpfs机制,能利用SQLite的锁机制,而不依赖PHP实现,我觉得很有优势很稳定.
这里讨论的不是Swoole这类CLI下实现的PHP服务,而是PHP传统的FastCGI模式.长时间后台常驻运行的PHP脚本,当然需要GC.对于一些要求实时的高并发应用,我觉得就不应该使用GC机制.PHP可以配置zend.enable_gc=off来禁用GC,并且自己通过unset来手动释放内存.不过运行在FastCGI下的PHP脚本生命周期很短,其实本来就不依赖GC.再次强调,高并发实时类应用,GC绝不是一个优势,而是一个劣势.
PHP常见的SAPI有这么几种:
php(cli,cli-server)
php-cgi(cgi-fcgi)
php-fpm/hhvm(fpm-fcgi)
libphp7.so/php7apache2_4.dll(apache2handler)
fpm-fcgi和apache2handler下,无论是多进程模式还是多线程模式,进程和线程的实现和管理都不需要PHP开发者关心(这是优势),而是由php-fpm/hhvm/apache实现.PHP开发者如果要参与多进程或多线程编程,完全可以在php-cli下实现,相关PECL扩展包括pcntl多进程,pthreads多线程,libevent事件驱动等等,相关的项目实现有WorkerMan.另外峰哥的Swoole也需要用php-cli跑,但其服务的进程和线程控制也是由Swoole实现而不太需要PHP开发者关心.
PHP从5.4内置的单进程HTTP服务器,目的就是用于快速的开发和测试,我觉得是一个很方便的工具,开发者不需要安装和配置Apache或者Nginx之类Web服务器就能进行入门开发.而且把PHP解释器交叉编译到Android手机或者OpenWRT无线路由就能用PHP这个省资源的HTTP服务器在局域网内编程并提供服务,很方便.
JIT在大量计算上有优势,bench.php脚本就是用来测试计算性能.真实应用如WordPress等,JIT能带来的性能提升肯定不会这么明显,具有JIT机制的HHVM和没有JIT的PHP7,在压力测试WordPress时体现出同一水平,就说明了这个问题.而且我强调过,Web应用大多是I/O密集型应用,编译型语言不会在I/O密集型应用里也具有数量级的优势.所以说,对于大多数PHP开发者来说,就算是没有JIT机制的PHP7,性能也够用了.
回复mem php-fpm && siege -c10 -t1M http://www.example.com/app/punbb/index.php > /dev/null && mem php-fpm
其中mem是我定义在~/.bashrc里的一个用于快速根据名称查看程序情况的函数:
mem ()
{
top -n1 -b | head -n7 | sed '1,6d' && top -n1 -b | sed '1,7d' | grep --color=auto -i $1;
ps aux | grep --color=auto -i $1 | grep --color=auto -v grep | awk -F " " '{ sum += $6 } END { printf "Total Memory Usage: %.1f MB\n", sum/1024 }'
}
用siege并发10,压测1分钟,PHP-FPM工作进程的内存(RES)都是13.4MB,并不存在你所谓的PHP-FPM内存占用会不断随请求数量而增长的问题,有图有真相:
![v2-958781c996161341a76ff014e1fb87e2_b.png](https://i-blog.csdnimg.cn/blog_migrate/85bec87bb2307e3667770766446e52c9.png)
![](https://i-blog.csdnimg.cn/blog_migrate/2dfb3cc89e59ae571faf92fbd30c5ae5.png)
鄙人工作需要,两种语言不断切换,就简单谈一谈两者区别
首先php确实是开发速度极快,为什么?因为弱类型(php7有类型声明选择开关,只要引入开关指令declare(strict_type=1)就会强制当前的文件下的程序遵循严格的参数类型,返回值类型),不需要定义返回的类型,加上php实际开发过程中喜欢运用它万能的数组来做计算,返回。所以他的动态扩展性非常强,如果返回值结构改变,没关系,直接改数组结构就行(注意,php数组太强大,可以当作map,list来用,底层的实现其实是hashmap)而java可能要开发过程可能要慢一点,java面向对象的技巧,设计模式会运用的更多一点。需要定义各类model来适应你业务的需求。编写风格会有一定的统一要求。强类型在编写的时候会比php麻烦,但好处是更安全,因为类型固定,潜在风险较低,还有编译器给你做了一次保障。