java比c_为什么说 Java 比 C / C++ 慢?

因为C/C++允许程序员做出更多选择。

选择更多,那么:

弊端:开发效率难以提高,因为有太多选择需要斟酌。

优势:执行效率可以逼近极限,因为不会有什么抽象拦住你。

举个例子吧:大家可能对Java无处不在的“对象.方法调用().方法调用().方法调用()”记忆犹新,论坛上也到处都是吐槽这个的。

当然,这个是显学,这里并不想讨论它。

不过,还有个东西藏在表面的繁复实现后面,可能很多人并不知道:函数调用非常浪费时间,这里面有大把的优化空间。

其中,函数使用的局部变量就是个很大的问题。

对于c/c++,以及其它任何“传统”编译型语言,都可以在函数内定义一个“局部变量”,这个局部变量默认分配在栈上——而栈是由CPU提供的专门指令硬件维护的。

因此,栈上数据的分配/删除速度极快(一条指令即可完成);而且栈空间是连续的,所以哪怕在不同函数间来回跳转,也不会出现缓存未命中问题;最后,因为“函数抽象”非常完善,所以这里的内存分配/回收完全无需用户干预。

当然了,这也带来了一些问题。比如程序员不得不区分栈上对象和堆上对象、遇到异常时栈上对象的释放也变得复杂而微妙……但总的来说,从这里得到的好处太大,困难与之相比,并不足论。

举例来说,3D API经常需要传递point2D或者point3D进去。

对任何“传统”语言,这都不是个问题:只要把这个小对象声明到栈上,然后调用相关API,完成。

甚至,对大多数编译器来说,很容易就能识别出“这是个需要传递给其它函数的局部变量”——于是,它会自动把这个point2D/point3D放进寄存器,从而连压栈/退栈指令都不需要执行!

你看,当我需要传递一个小对象时,用c/c++,压根无需思考,声明成局部变量,编译器就把该做的事情全帮我做了——传递它,除了会占用几个通用寄存器,我甚至无需付出哪怕一条指令的代价!

哪怕最差情况,传参的代价也不过是执行了几次push/pop这两条机器指令而已。

但,对java 6之前的版本来说,除了基本类型的变量,其它任何用户自定义的东西,全都默认且只能放在堆上——然后通过栈上的指针间接访问。

这是个很要命的问题。

首先,堆上分配/回收,至少要比栈上分配多执行十条甚至百条指令;其次,东一榔头西一锤的访问七零八落的堆上对象极度缓存不友好;再次,通过指针访问堆内存,需要执行间接寻址指令,这种指令执行速度很慢;最后,基本不可能把它优化成寄存器对象。

你看,不知不觉间,仅一个小对象(结构体)传递,c/c++就能比java快至少10倍、多则百倍甚至数百倍以上。

而且,在这个场景里,c/c++程序员几乎不需要付出任何代价。他们只需简单的把小对象声明成局部对象即可——无需关心其创生,也无需关心其回收。

想一下吧,这种小对象传递,在底层系统会有多么频繁。一旦每次都要付出十倍百倍千倍的代价……

Java本身宁可担着“基本类型不是对象”的骂名,也要保留它们,就是为了利用栈。不然性能太难看。至于小对象……那就只好顾头不顾腚了……

PS:Java 6之后有了逃逸分析,可以自动识别出临时变量,从而将其置于栈上。不过逃逸分析也有局限,必须放JIT执行。这是因为有些类可能会在运行期被替换,使得静态分析失败。感谢评论区 @Glavo 指出错误。

现在知道那个java吹为什么会对图形学深恶痛绝了吧?“而且因为图形学搞游戏在主流技术界看来,属于歪门邪道,不务正业,跟黄赌毒没有太大差别,计算机的本质应该是服务于科学计算,而不是给小孩子玩游戏用的“

其实,这类细节比比皆是……

比如说,底层系统经常要处理海量的、同类型的小对象。其中相当一些是不能放在栈上的——毕竟数量太庞大了。

这时候呢,作为c/c++程序员,手撸一个“xx对象专用内存池”可是小菜一碟——我就曾花几十分钟时间撸过一个泛型版本的,支持多线程(没对单线程做过优化)。

你猜这个内存池分配/回收效率能有多高?

我的实测结果是:它比c/c++自己的malloc/free以及new/delete快40多倍。

这还是花几十分钟随手撸的,并不是lock free版。

并且,通过union,这个内存池并不需要付出任何额外的空间代价(只有打开DEBUG宏时,会给每个对象额外分配4个字节的空间,以存储一些状态/跟踪信息,方便排除错误)。

——这也从另一个侧面,证明了堆内存分配能有多慢:专用内存池再轻量,分配一次内存也得加锁解锁查找空闲块什么的折腾一遭,和栈上硬件支持的push/pop指令完全没得比。

你说java?它敢和c/c++那个通用版的内存分配比综合效率吗?这里也修正一下。Java的内存分配是典型的空间换速度。这导致Java程序普遍需要5倍的内存才能得到同样的执行速度(在这个时候它甚至比c/c++内存分配速度更快,毕竟c/c++是不敢替程序员决定是“速度换空间”好呢、还是“空间换速度”好:选择权在你;或者说,选择的包袱就丢给你了)。

要是没这点好处,你以为c/c++程序员都是受虐狂,偏要用这种老掉牙的危险的人类不友好的奇葩工具啊。

总之,每一种语言都有其“激进”之处(比如c/c++就以“不怕程序员麻烦”著称),但它们做出的每一点取舍往往都是有自己的理由的。

同样的,根据自己面对的问题做出取舍,选择最合适的语言,也是一门学问。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值