一些杂想:Java老矣,尚能饭否?( 六 )


以一个常见的Point类为例:
final class Point {final int x;final int y;}
一个Point对象数组在内存中的布局是长这样的:

一些杂想:Java老矣,尚能饭否?

文章插图
为了提升性能 , 有的小伙伴可能会用“曲线救国”的方法 , 把Point[] pts变成两个int数组int[] xs和int[] ys , 这就成"Good Code"和" Code"的两难选择了 。
引入的值类型有点向C#中的偷师的味道 。值类型的想法是 , 像Point一类的对象 , 本质上是纯数据的聚合 , 只有数据 , 没有标识 。没有标识意味着不再有多态性、可变性 , 不能在对象上加锁 , 不能为Null , 只能基于状态做对象比较 , 但优势是:
可以使用关键词定义一个值类型:
inline public class Point {public int x;public int y;public Point(int x, int y) {this.x = x;this.y = y;}}
值类型的内存布局长这样:
看上去值类型跟基础类型很像(某些小伙伴要说了 , 这跟我之前干的用两个int[]来代替Point[]的方式有什么区别?) , 不同之处在于可以将其看做一种可以快速访问的带限制的特殊对象 , 因此有对象的特征(Codes like a class, works like an int) , 比如:
有了值类型的支持后 , 的另一个JEP:overTypes [56]就很自然了 , Java 泛型中令人诟病的不支持原数据类型( Type)、频繁装箱等问题也能迎刃而解了 。想象一下你只是需要一个数字列表 , 然后只能被定义成一个 。对于API设计者 , 也不用再搞什么和了 。
最后说一点 , 一个值类型看似简单 , 实际上创建一种新的数据类型需要对编译器、类文件结构和 JVM 都进行更改 , 还要支持现有的库 , 譬如、等 。从14年到现在 , Java 团队已经对六种同的解决方案进行了原型设计 , 值类型(value types)这一术语也被重命名为内联类( ) , 然后又变成原始类( ) 。总之 , 耐心等待吧…
队友的助攻
Java最牛逼的是什么 , 是它的生态圈和圈里的队友们啊 。我列了几个我觉得比较有代表性的 。
在18年官宣了[57]的1.0版本 。虽然名字里带着VM , 但实际上它既是的新型 JIT 编译器[58] , 又可以用作AOT编译器 , 也是一个新的多语言虚拟机 。有3个关键的组件:
算是近年来的明星Java项目 , 发展很快 。这里我只做个简单的介绍 , 感兴趣的同学建议直接上官网[60]看官方文档 。
Graal
我们熟知的有两个JIT编译器 , C1和C2 。Java 程序首先在解释模式下启动 , 执行一段时间后 , 经常被调用的方法会被识别出来 , 并使用 JIT 编译器进行编译——先是使用 C1 , 如果检测到这些方法有更多的调用 , 就使用 C2 重新编译这些方法 。这种策略被称为“分层编译” , 是默认采用的方式 。经过这么多年优化下来 , C2编译后的代码效率非常出色 , 可以与 C++ 相媲美(甚至更快) 。不过 , 近年来 C2 并没有带来多少重大的改进 。不仅如此 , C2 中的代码变得越来越难以维护和扩展 , 新加入的工程师很难修改使用 C++ 特定方言编写的代码 。
Graal编译器的目标之一就是替代C2 , 因此这两者难免会拿来做比较 。可以说最明显的区别就是Graal是用Java写的 , C2则是C++ 。一种普遍的看法(来自 等公司和 Cliff Click 等专家)认为 , C2在当前设计中不可能再进行重大改进 , 而Graal使用Java开发的一大优势在于可以很方便地将C2的新优化移植到Graal中 , 反之则不然 , 比如 , 在Graal中被证实有效的部分逃逸分析()至今未被移植到C2中 。