由Java的破解和反破解想到的JVM和.Net的实现

JVM就像仿真的处理器,它内部的工作协同和汇编还是比较相似的,只是一个机器指令层级,也就是说,它只有指令逻辑,没有语法逻辑,如果你对字节码技术足够了解,也可以编出自己的字节码文件,让虚拟机执行,也就是如上文所说的绕过javac(java编译器)的语法检查和语义分析。
java平台是很明显的多层结构:java语言层+字节码指令层+jvm内部
除了java语言层,下面的两个层次采用可能涉及操作系统的方式编写的虚拟机器实现,猜想sun做芯片和服务器的实力可以在这里的机器指令中得到体现,往下走需要一批对平台相关特性熟悉的人,往上走则需要在编程语言上大师级的人物。

说到这里,不妨再说说它的发展,java芯片和处理器直接替换了底下的两层(也可能并不完全替换,而是保留一层薄薄的优化和通用)故而java字节码就可以如同机器语言般在java真实机上跑的欢快,这个程度是很理想的,但是能否实现依旧还是未知数,到那时,sun或许会变成另一个apple,由于java的跨平台,还是要风光一点的,不过老实说,一直对sun的实力没什么信心呀。

搞清了平台的层次,却还需知道java语言本身尽管已经拥有相当的健壮性和稳定性,却仍然并不完善,网上找到的 java问题几乎也和java优点一样多了,平台技术可以不断优化,但语言级别却严重地受扩展性,向前向后兼容性影响,也包括历史原因,慢慢的,无法适应新的编程需求了(我说的是有一天,不是现在),那时三层级的平台需要做外科+内科手术般的改造,可以看到,由于下面两层的机器相关性决定了不可能也不需要做过多的调整(基本上是最优的了),则java语言级别可以进行更适应需要的改进,包括语法糖,新语法机制的引入,编译优化和编译检查,这样就可以进一步地提升语言级别的功能,得到如同进化般的效果。不得不提的却是.Net,微软学java学得可谓坚决,采用了很相似的构架,托管的方法,跨语言,这其实和java是一样的道理,都是希望实现最大范围的通用性和最灵活的适用性。当然,微软无疑会把.Net锁定在Windows下,以提高一定程度上的执行速率和对旧程序旧代码的支持。

可惜CLR并不是可探察的,否则支持将java编译成IL的编译器并不难写,但支持C#编译成字节码的编译器却是无论如何都难以实现的,这二者之间的互相转换为什么不是对称性的呢? 原因如下:

二者除了语言级别的相似,最重要的,也是最为人们所认可的,就是都拥有强大的类库,可以说,java的类库百分之九十是纯字节码文件实现,它是自己动手,丰衣足食的,因此速度受虚拟机制约,同时也保有最大的灵活性(参考swing,这个灵活性由于跨平台更为显著),而.Net,则主要是由win32api实现,加上薄薄的类封装(这也是.Net平台中类的关系相对简单的缘故),故而速度更快,也随着Windows的改进而可以进一步完善甚至替换(当然,由于面向对象的封装,这对用户可以做到最小的影响甚至察觉不到,也由于win32api的演化内在的规律性和一脉相承,这是顺理成章而且合适的),二者的发展前途都很广阔,这个不是我们讨论的重点,就讲到这里吧。

那写出由java编译得到的.Net平台编译器呢,由于java类库的独立性,只要从根部的几个类实现和.Net平台的很好衔接,就可以保证整个类库移植的稳定性,从而利用java丰富的代码库的同时,享受在Windows下的优待(执行速度一定程度的上升,Windows外观和响应,当然也有.Net的虚拟机支持,跨语言的类库调用等,当然,那时最头疼的可能就是编出的程序会遇到跨平台的障碍——使用了.Net类库的原因)。注:这里其实还是有不少问题和文章可以研究的,比如,在基础数据类型的转换上,在数组和类特性上的差异等,但这毕竟时语言级别的,对于底层类似机器的执行方式不变,总还能找到解决的办法,.Net的跨语言性质也决定了它会在这方面有更大的兼容性。

至于写C#等语言在jvm的编译成字节码的编译器,读者应该可以想象得到其障碍是多么巨大了吧,是啊,大量平台相关的api,如何用字节码实现,效率上一反一复再一反必然及其低下,绝对是吃力不讨好的事情,更不用说想实现跨平台了,这也只是针对C#,要是VB或C++还会有更多的问题吧。

总的说来,java和.Net同样是希望实现在编程环节和执行环节耦合的可适用性扩展,而实现的在操作系统层次之上和之内的不同的的平台技术,它们结构中相似的层次性,也正是更先进的编程平台技术的特点:编程与执行的分离,对编程人员和用户的双向友好。

有时候我想,我是否吧虚拟机底层考虑得过于简单化了,它明显是很接近真实机的一种仿真,但为了支持上层语言,特别是面向对象的一些特性,是否还做了一些特殊的处理,在近乎汇编级别的字节码层面上如何实现一种围绕语言特性而做的抽象呢,我想跨语言的.Net应该更具参照和借鉴的价值吧,毕竟它做的是语言的中立,也许,这也有可能制约了它的效率。

 

总是会看到网上的口水论战C++/java/,Net,也听到很多人说语言本身并不重要,重要的是思想,一心埋头学好技术,不管它是什么都还是有一席之地的,个人很佩服他们,但是做不到那种境界,所以每每看到诋毁我的语言的做法都不能接受,所以才有了下面的段落。

java的公认优势是跨平台,但其实,能够跨操作系统,却掩盖了它的真正优势,那就是,为跨操作系统而实现的虚拟机。

虚拟机给人的第一感觉就是慢,由慢出发去寻找原因很容易就联想到了以下两点,垃圾回收,解释执行。

各种语言程序在内存上的使用和管理都有各自不同的实现,如C++的析构,java的垃圾回收,但析构真的更好吗?其实不见得,析构释放了内存空间,但并不表示内存空间即为可用。举个简单的例子,一群动物坐公车,有的很大有的很小(这类似于内存中分配的空间差异),要合适的安排每个动物的座位并在其中一个下车的时候方便地让其他动物补上位子是很困难的,因为大动物小的位子是坐不下的,小动物坐大的位子就浪费了,而同时挤几个小动物又总是难以恰当地利用(这里忽略弹性所发挥的作用,因为无论是内存还是真实世界弹性都是基于真实的有限空间上的预先支取)。同样析构的实现只能便利于所需空间与之匹配或是略小一点点的单元,而让析构的单位保持很小或很大的粒度都无法使内存获得更合理的应用,参考公车让座情况。也许好的数据结构(如链表)的使用能让你在一定程度上缓解这一点,但稍微大一点的程序都需要各种各样的数据结构,不同结构之间的内存支配方式是不同甚至是抵触的,而程序总是用一块有限的内存兼容各种数据结构(尽管会有堆和栈的区别,但总空间是限定的),这就是纯析构的C++程序越往大的走,就越缺失对内存的统筹规划的原因。剜出一块内存也许在一个程序片断看来是尽到了对程序的义务,其实这并不像它希望的那样支持了程序的整体调优。不止是内存,析构间接体现的是C++设计者的思想,即做高效率的程序,对底层绝对的把握,和正确的程序员,其实这没什么错,只是,再精锐的军队,都无法保证领导人思想的绝对正确,无法保证在军队面前最有利的行动对整个战略局势甚至政治层面上的一致性,所以军队需要灵魂,那就是政治素养,是政治的领导,是对全局观的把握,那样,即使在装备和训练上稍逊一筹,仍然是有潜力,能走向胜利的队伍。

回头看垃圾回收,它不快,而且是C++实现的(估计底层也会涉及到大的内存块的析构),它只做一件事,就是收集有用的东西(而不是没用的),并对内存空间进行再分配,使释放的内存(指没有引用的对象)能够重归于整体。形象点,就是把有用的内存单元剜出来,然后一个个填在新分配的崭新内存区域中,这一行为看似比剜出没用的要费劲,起码小程序如此,然而随着程序越大越复杂,涉及的临时空间分配越多而越显出它的优势,垃圾回收对内存的管理能力是简单析构所无法达到的,而随着程序复杂度的提升,它还会有一定的效率上升空间。它借助的是特定的算法来决定垃圾回收的时机和频率,实际上是在用算法和程序员的主动编程思想比拼对内存管理的控制能力,这二者同归于思想的延伸,同随着新思路的拓宽而改进,随复杂度的提升而复杂,只是,你愿意选择何者呢?

java其实谈不上是解释执行的语言,由本文最上面的虚拟机理论可以很清楚地看出,对于虚拟机它就是实际上的机器语言,语义上和汇编很相近,在剥开字节码的外衣并为对象采用堆创建之后如果机器指令那样执行着,其实无所谓解释执行还是编译执行,它们对机器的定义不同,故而才呈现了不同的表象,只是,你可以以很简单的方式创建类文件,高效地编译字节码,更有甚者绕过javac直接写字节码指令,但你能对真实的机器这么做吗?它允许拥有合适的调试方式吗?它能保证出错之后对系统和机器的危害到达最小的程度,能保证在这个机器上写的指令在那台机器上仍旧可用吗?机器语言不行,其他语言也无法完全做到,java可以,不但是实在的类文件获得的字节码,真正的虚拟机指令,也是通用的。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值