首页
学习
活动
专区
工具
TVP
发布
精选内容/技术社群/优惠产品,尽在小程序
立即前往
您找到你想要的搜索结果了吗?
是的
没有找到

Reactjs+BootStrap开发自制编程语言Monkey的编译器:发刊词

编译原理几乎是计算机专业中最晦涩难懂的课程。很多学生学这门课只不过是为了通过考试,学完后对编译原理之精妙仍然是摸不着头脑。而很多教这门课的老师,也只不过是混口饭吃,他自己未必对编译原理有多少深入的了解和把握,于是与其昏昏,使人昭昭。毕业多年后,回过头来反省我所承受的教育,我发现我们的教育总是流于表面的肤浅,给学生展示的始终是冰山的一角,对冰山下的巨大形体置若罔闻,于是整个系统虽然培养出大量的计算机专业人员,但有能力对计算机知识具备深入见解的人凤毛麟角,很多人其实是走上工作岗位后,通过大量的生产实践才开始对计

04

笔记:追随云原生的Java

但在微服务时代是提倡服务围绕业务能力(不同的语言适合不同的业务场景)而非技术来构建应用,不再追求实现上的一致,一个系统由不同语言、不同技术框架所实现的服务来组成是完全合理的。服务化拆分后,很可能单个微服务不再需要再面对数十、数百 GB 乃至 TB 的内存。有了高可用的服务集群,也无须追求单个服务要 7×24 小时不可间断地运行,它们随时可以中断和更新。不仅如此,微服务对镜像体积、内存消耗、启动速度,以及达到最高性能的时间等方面提出了新的要求。这两年的网红概念 Serverless(以及衍生出来的Faas) 也进一步增加这些因素的考虑权重,而这些却正好都是 Java 的弱项:哪怕再小的 Java 程序也要带着厚重的Rumtime(Vm和StandLibrary)——基于 Java 虚拟机的执行机制,使得任何 Java 的程序都会有固定的内存开销与启动时间,而且 Java 生态中广泛采用的依赖注入进一步将启动时间拉长,使得容器的冷启动时间很难缩短。 举两个例子。软件工业中已经出现过不止一起因 Java 这些弱点而导致失败的案例。如 JRuby 编写的 Logstash,原本是同时承担部署在节点上的收集端(Shipper)和专门转换处理的服务端(Master)的职责,后来因为资源占用的原因,被 Elstaic.co 用 Golang 的 Filebeat 代替了 Shipper 部分的职能。又如 Scala 语言编写的边车代理 Linkerd,作为服务网格概念的提出者,却最终被 Envoy 所取代,其主要弱点之一也是由于 Java 虚拟机的资源消耗所带来的劣势。 1.变革之火 1.1 Complie Native Code 显然,如果将字节码直接编译成可以脱离 Java 虚拟机的原生代码则可以解决所有问题。 如果真的能够生成脱离 Java 虚拟机运行的原生程序,将意味着启动时间长的问题能够彻底解决,因为此时已经不存在初始化虚拟机和类加载的过程。也意味着程序马上就能达到最佳的性能,因为此时已经不存在即时编译器运行时编译,所有代码都是在编译期编译和优化好的。同理,厚重的Runtime也不会出现在镜像中。 Java 并非没有尝试走过这条路。从GCJ到 Excelsior JET再到 GraalVM 中的 SubstrateVM 模块再到 2020 年中期建立的 Leyden 项目,都在朝着提前编译(Ahead-of-Time Compilation,AOT)生成原生程序这个目标迈进。Java 支持提前编译最大的困难在于它是一门动态链接的语言,它假设程序的代码空间是开放的(Open World),允许在程序的任何时候通过类加载器去加载新的类,作为程序的一部分运行。要进行提前编译,就必须放弃这部分动态性,假设程序的代码空间是封闭的(Closed World),所有要运行的代码都必须在编译期全部可知。 这一点不仅仅影响到了类加载器的正常运作,除了无法再动态加载外,反射(通过反射可以调用在编译期不可知的方法)、动态代理、字节码生成库(如 CGLib)等一切会运行时产生新代码的功能都不再可用——如果将这些基础能力直接抽离掉,Hello world 还是能跑起来,大部分的生产力工具都跑不起来,整个 Java 生态中绝大多数上层建筑都会轰然崩塌。随便列两个Case:Flink的SQL API会解析SQL并生成执行计划,这个时候会通过JavaCC动态生成类加载到代码空间中去;Spring也有类似的情况,当AOP通过动态代理的方式去生成相关逻辑时,本质还是在Runtime时生成代码并加载进去。 要获得有实用价值的提前编译能力,只有依靠提前编译器、组件类库和开发者三方一起协同才可能办到——可以参考Quarkus。 Quarkus和我们上述的方法如出一辙,以Dependency Inject为例:所有要运行的代码都必须在编译期全部可知,在编译期就推导出来相关的Bean,最后交给 GraalVM来运行。 1.2 Memory Access Efficiency Improvement Java 即时编译器的优化效果拔群,但是由于 Java“一切皆为对象”的前提假设,导致它在处理一系列不同类型的小对象时,内存访问性能很差。这点是 Java 在游戏、图形处理等领域一直难有建树的重要制约因素,也是 Java 建立 Valhalla 项目的目标初衷。 这里举个例子来说明此问题,如果我想描述空间里面若干条线段的集合,在 Java 中定义的代码会是这样的: public record Point(float x, float y, float z) {} public record Line(Point start, Point end) {} Line[] lines; 面向对象的内存布局中,对象标识符(Object Ident

01
领券