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


标记-复制的算法需要有两块内存区域 , 一个典型的例子是新生代的区;标记-清除的算法很多时候同样需要更大的内存区域 , 因为在GC结束时会有大量的空间碎片 , 在分配大对象时会很麻烦 。
像CMS/G1这样的并发回收器 , 因为在垃圾收集阶段用户线程还需要持续运行 , 那就需要预留足够内存空间提供给用户线程使用 。
CMS的做法是在老年代达到指定的占用率后(Java 6后默认为92%)开始GC , 可以通过-XX:参数调高这个值 , 但调得太高又容易碰到 Mode ;
G1的解法则是为每一个设计了两个名为TAMS(Top at Mark Start)的指针 , 把中的一部分空间划分出来用于并发回收过程中的新对象分配 , 并发回收时新分配的对象地址都必须要在这两个指针位置以上 , 并且默认不回收在这个地址以上的对象 。
一般来说 , JVM在启动时就会一次性申请大块内存(上图的 Heap) , 然后倾向于在运行期保留这些内存 。虽然一次GC结束后可能会空出很多内存 , 但JVM在内存返还策略上有时会左右为难 , 因为这些内存有可能很快就需要被拿来分配对象 , 如果频繁进行归还 , 再而触发 page fault 反而带来性能下降 。折中的策略是动态地根据负载来决定是否返还 。
在这之前 , G1只有在Full-GC或并发周期期间才能返还内存 , 而G1的目标之一是避免Full-GC , 并且仅根据 Java 堆占用和分配活动触发并发循环 , 因此多数场景下 , 除非强制触发 , 并不会有内存返回行为 。在Java 12后 , G1会在应用不活动的空闲期间定期尝试继续或触发并发循环以确定整体 Java 堆使用情况 , 并自动将 Java 堆中未使用的部分返回给操作系统 。

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

文章插图
JEP中举了一个服务器的示例 , 服务器在白天提供HTTP请求 , 而在夜间大部分时间处于空闲状态 , 新的内存返还特性可以使得JVM提交的内存减少85% 。
Java 13:小升级+1
同Java 10一样 , Java 13也是一个小升级版本:
Java 14:小升级+2
Java 15:ZGC和转正
从Java 11和Java 12分别引入ZGC和以来 , 一直是的两大垃圾回收器终于了 。
Java 16: Linux的支持
Java 16中跟性能提升相关的特性主要包括:
另外值得一提的是Java 16将JDK移植到了 Linux[26] 。Linux[27]是一个非常轻量的Linux发行版 , 其镜像只有5MB左右(对比系列镜像接近200 MB) 。更小的镜像意味着容器环境中更小的磁盘占用和更快的镜像拉取速度 , 正因如此 ,  官方已开始推荐使用替代之前的作为基础镜像 。为了瘦身 ,  Linux默认是用musl[28]而非传统的glibc作为C标准库 , 因此之前的JDK并不直接支持 , 而是需要在基础上安装glibc 。
基于 Linux基础镜像 , 再结合Java 9引入的模块化能力 , 如果程序只依赖 java.base模块 , 镜像的大小可以小至38 MB 。
Java 17:最新的LTS版本
激进的团队可能会跳过Java 11 , 直接从Java 8升级到Java 17 , 因为这是最新的LTS版本 。Java 17(包括最新的Java 18)本身并没有包含太多的性能提升特性 , 更多的是语法和API的变动 , 也没啥好列的了 。
X
标题的 X只是代称 , 代表了Java官方或社区所推进的一系列项目 。这些项目出于不同的动机 , 但最终的目的都是为了让Java更适应新的时代 。完整的项目列表可以看这里[29] , 其中比较有代表性的有: