【Java入门教程】Java 简介

Java简介

本系列教程基于Java版本1.8

概述

Java是一种广泛使用的编程语言,由Sun Microsystems公司于1995年推出。它是一种面向对象的、跨平台的编程语言,具有良好的可移植性和可扩展性,因此在软件开发领域得到了广泛应用。本文将通过详细介绍Java的特点、应用领域以及发展历程等方面,帮助读者了解Java的基本知识和相关概念。

Java的特点

  1. 面向对象:Java是一种纯粹的面向对象编程语言,支持封装、继承和多态等特性。面向对象的编程模型使得Java代码更加易于理解、维护和扩展。

  2. 跨平台性:Java的跨平台性是其最重要的特点之一。通过Java虚拟机(JVM),Java程序可以在不同的操作系统上运行,只要相应平台上有对应的JVM。这使得Java成为一种具有广泛适用性的编程语言。

  3. 健壮性:Java具有良好的错误处理和异常机制,通过先进的垃圾回收机制自动管理内存,大大减少了程序运行过程中的崩溃和错误。

  4. 安全性:Java提供了强大的安全机制,可以防止恶意代码的执行和访问系统资源。Java的安全性使其成为开发网络应用和移动应用的首选语言。

  5. 高性能:尽管Java是一种解释型语言,但通过即时编译技术和优化手段,Java的性能接近于编译型语言。Java虚拟机具有自动内存管理、即时编译和多线程等优点,使得Java程序在性能方面有很好的表现。

Java的应用领域

Java作为一种通用编程语言,广泛应用于各个领域。下面是一些常见的Java应用场景:

  1. 企业级应用:Java提供了一套完整的企业级开发框架和技术,如JavaEE(Java Enterprise Edition),使得开发大规模、高性能的企业应用变得更加容易。

  2. 移动应用:随着智能手机的普及,Java在移动应用开发领域也得到了广泛应用。Android平台采用Java作为主要开发语言,使得开发Android应用变得简单且高效。

  3. 嵌入式系统:由于Java具有跨平台性和高性能,它在嵌入式系统开发中也得到了广泛应用。例如,智能家居、汽车电子和工业自动化等领域都使用Java来开发嵌入式软件。

  4. 大数据:Java在大数据处理和分析领域也扮演着重要角色。Hadoop、Spark和Storm等大数据处理框架都支持Java编程,使得开发大规模数据处理应用更加便捷。

  5. 云计算:随着云计算的快速发展,Java在云平台开发中也有很多应用。Java的可移植性和安全性使得它成为云平台开发的首选语言。

提示:下列内容过多,不感兴趣或者初学者可以直接跳过本章,不影响接下来的学习!

Java的发展历程

Java是在1991年由SUN公司的James Gosling(Java之父)及其团队所研发的一种编程语言,第一个版本耗时18个月,最开始命名为Oak(一种橡树)。Java现在广泛应用于各种大型互联网应用,其设计的最初动机主要是平台独立(即体系结构中立)语言的需要,可以嵌入到各种消费类电子设备(家用电器等),但市场反应不佳。

随着1990年代互联网的发展,SUN公司看到了Oak在互联网上的应用场景,在1995年更名为Java(印度尼西亚爪哇岛的英文名称,因盛产咖啡而闻名),随着互联网的崛起,Java逐渐称为重要的Web应用开发语言。Java的发展可以主要看JavaWeb的发展,Java也见证了互联网的发展过程。

发展至今,Java不仅是一门编程语言,还是一个由一系列计算机软件和规范组成的技术体系,Java 是几乎所有类型的网络应用程序的基础,也是开发和提供嵌入式和移动应用程序、游戏、基于 Web 的内容和企业软件的全球标准。

从笔记本电脑到数据中心,从游戏控制台到科学超级计算机,从手机到互联网,Java 无处不在!

  • 97% 的企业桌面运行 Java
  • 美国有 89% 的桌面(或计算机)运行 Java
  • 全球有 900 万 Java 开发人员
  • 开发人员的头号选择
  • 排名第一的部署平台
  • 有 30 亿部移动电话运行 Java
  • 100% 的蓝光盘播放器附带了 Java
  • 有 50 亿张 Java 卡在使用
  • 1.25 亿台 TV 设备运行 Java
  • 前 5 个原始设备制造商均提供了 Java ME

数据来源:https://www.java.com/zh_CN/about/ ​

说到Java自然离不开JDK、JVM、JRE,三者有什么关系。

  • JDK(Java Development Kit)Java开发工具包,包含Java语言、Java虚拟机、Java类库,是支持Java程序开发的最小环境。
  • JVM(Java Virtual Machine)Java虚拟机,运行于各种操作系统Linux,Windows,Solaris等之上,执行编译好的Java字节码class文件。
  • JRE(Java Runtime Environment)Java运行时环境,包含JavaSE中核心类库API和Java虚拟机,简单理解为JVM+核心类库API。

它们三者的关系下面的文章将会详细介绍 JDK、JVM、JRE关系图

下面将是关于JDK、JVM、GC算法的发展历程和介绍,如果您是初学者可以不关注这些细节,不感兴趣直接跳过即可。

JDK发展史

1991年4月,Java之父James Gosling带领绿色计划(Green Project)项目启动,定位于消费电子产品(机顶盒、冰箱、收音机)运行架构的Oak语言诞生,这也是Java的前身,但是市场反响一般。

  • 1995年5月23日,随着互联网浪潮在1995年兴起,Oak迅速蜕,Java语言诞生,在SunWorld大会上发布Java1.0,第一次提出Write Once,Run Anywhere的口号。
  • 1996年1月23日,JDK1.0发布,纯解释型的Java虚拟机(Sun Classic VM)、Applet、AWT等诞生。
  • 1996年4月,十个最主要的操作系统和计算机供应商声明将在其产品中嵌入Java技术,
  • 1996年5月底,Sun于美国旧金山举行了首届JavaOne大会,从此JavaOne成为全世界数百万Java开发者每年一度的技术盛会。
  • 1996年9月,已有大约8.3万个网页应用了Java技术来制作。
  • 1997年2月19日,Sun公司发布了JDK1.1,代表技术:JAR文件格式、JDBC、JavaBeans、RMI等,Java语法也进行了增强,内部类(Inner Class)和反射(Reflection)出现。
  • 1998年12月4日,JDK1.2发布,这是一个里程碑式的重要版本,工程代号为Playground(竞技场),这个版本代表性技术非常多,如EJB、JavaPlug-in、Swing、JavaIDL等,还有使用极为频繁的Collections体系工具类等,并且这个版本中Java虚拟机第一次内置了JIT(Just In Time)即时编译器,后续还发布了JDK1.2.1和JDK1.2.2两个小版本升级。在JDK1.2中曾经共存过ClassicVM、HotSpotVM、ExactVM三个虚拟机,其中HotSpot是作为附加程序提供。也是在这个版本中Sun开始拆分对应的Java产品线,Sun在这个版本中把Java技术体系拆分为三个方向:
  • 分别是面向桌面应用开发的J2SE(Java 2 Platform,Standard Edition)
    • 面向企业级开发的J2EE(Java 2 Platform,Enterprise Edition)
    • 面向手机等移动终端开发的J2ME(Java 2 Platform,Micro Edition)
    • 1999年4月27日,HotSpot虚拟机诞生,HotSpot最初由Longview Techno-logies公司研发,后来在1997年被Sun公司收购,后来它成为JDK 1.3及之后所有JDK版本的默认Java虚拟机。
  • 2000年5月8日,工程代号为Kestrel(美洲红隼)的JDK 1.3发布。JDK 1.3的改进主要体现在Java类库上(如数学运算和新的Timer API等),此外一直作为扩展服务的JNDI服务也开始作为一项平台级服务被提供,还有基于CORBA IIOP协议来实现通信段RMI也出现了。
  • 2001年5月17日,JDK1.3的修订版JDK1.3.1发布,工程代号Ladybird(瓢虫)。从JDK1.3开始,Sun公司维持着稳定的开发节奏,大概每个两年发布一个主要版本,期间发布的各个修订版本以昆虫作为工程代号。
  • 2002年2月13日,JDK 1.4发布,工程代号为Merlin(灰背隼)。JDK 1.4是标志着Java真正走向成熟的一个版本。Compaq、SAS、Fujitsu、Symbian、IBM等一众大公司参与功能规划,甚至实现自己的独立发行版本。哪怕在二十年后的今天一些主流功能也可以在JDK1.4上运行,这个版本的主要代表技术包含正则表达式、异常链、NIO、日志类、XML解析器和XSLT转换器等等。
  • 2002年9月16日,工程代号为Grasshopper(蚱蜢)的JDK1.4.1修订版发布。在这一年前后微软平台的.NET Framework发布,至此Java平台和.NET平台的竞争开始拉开了序幕。但似乎Java的开源策略更胜一筹,终于在2014 年 11 月 12 日,微软正式宣布了.NET Core 的开源。
  • 2003年6月26日,工程代号为Mantis(螳螂)的JDK1.4.2修订版发布。
  • 2004年9月30日,JDK 5发布,工程代号为Tiger(老虎),Sun公司从这个版本开始放弃JDK 1.x的命名方式,将产品版本号修改成了JDK x,JDK1.2以来Java语言在语法上的改动都不大,该版本在语法易用性上做出了非常大的改进,如自动装箱、泛型、动态注解、枚举、可变长参数、foreach等。此外改进了Java的内存模型(Java Memory Model,JMM)、提供了java.util.concurrent并发包(由Doug Lea大师带Java进入了并发时代)等,JDK 5是官方声明可以支持Windows 9x操作系统的最后一个版本。
  • 2006年11月13日,JavaOne大会上,Sun公司宣布计划要把Java开源,随后在一年多的时间内,陆续的将JDK各部分在GPL V2协议下公开源码,随后并建立了OpenJDK组织对这些源码进行独立管理,除了极少部分的产权代码,OpenJDK几乎拥有了SunJDK 7中的全部代码。
  • 2006年12月11日,JDK6发布,工程代号为Mustang(野马)。在这个版本中,Sun公司终结了从JDK 1.2开始已经有八年历史的J2EE、J2SE、J2ME的产品线命名方式,启用Java EE 6、Java SE 6、Java ME 6的新命名来代替。在JDK 6中提供了众多改进,如通过Mozilla JavaScript Rhino引擎提供初步动态语言支持,提供编译器注解处理器(Annotation Processor这也是Lombok的原理,通过注解生成模板代码)和微型HTTP服务器API,以及对虚拟机内部锁、同垃圾收集、类加载机制等方面进行了大量优化改动。在JDK 6发布以后由于代码的复杂化,Java开源、开发JavaFx、世界经济危机以及Oracle对Sun的收购提案等原因,Sun公司内忧外患自顾不暇,原本稳定的开发进度也受到了很大的影响,使得JDK 6的生命周期也持续了很久,一共发布了211个更新补丁,最终版本为Java SE 6 Update 211,于2018年10月18日发布。
  • 2009年2月19日,工程代号为Dolphin(海豚)的JDK 7发布,这是其第一个里程碑版本,按照规划,共有十个里程碑版本发布,最后一个里程碑版本发布与2010年9月9日,由于各种原因JDK 7没有按照原计划完成。JDK 7开发阶段Sun公司在技术竞争和商业竞争中都深陷泥潭,已经无力推动开发进展,为了尽快完成JDK 7的发布,因此裁掉了大部分原定的功能,对于原定的Lambdax项目、Jigsaw项目、动态语言支持、Gabage-First垃圾收集器、Coin项目只匆匆交付了处于Experimental状态的G1垃圾收集器(直到2012年4月的Update 4中才开始正式商用),其余项目延期到JDK 8中。Oracle从JDK 7开始进行接手,迅速展现出了其极具商业化的处世风格,面对Java中使用最广泛的Java SE免费产品线,定义了一套新的商业版产品Java SE Support ,此外JDK 7计划维护到2022年,已经面向付费用户发布了211个补丁,最新版本为JDK 7 Update 211。
  • 2009年4月20日,Oracle宣布正式以74亿美元的价格收SUN公司,一代巨头由此没落,Java商标正式划归Oracle所有,Java语言本身并不属于哪间公司所有,它由JCP组织进行管理。此外Oracle还收购了BEA公司,JavaEE服务器Weblogic就是该公司的产品。
  • 2011年7月28日,JDK7发布,做出的改进:提供新的G1收集器、加强对非Java语言的调用支持、可并行的类加载架构等。
  • 2014年3月18日,Oracle公司发布JDK 8,从此使用JEP(JDK Enhancement Proposals)来定义和管理纳入新版JDK发布范围的功能特性,JDK 8中实现了JDK7中定义并且未完成的功能,其中也有被延期到JDK 9的Jigsaw模块化功能。
    • JEP 126 Lambda函数表达式支持
    • JEP 104 内置Nashorn JavaScript引擎
    • JEP 150 新的时间日期API
    • JEP 122 移除HotSpt永久代
  • 2017年9月21日,Oracle公司发表 JDK 9,这其中较为显著的功能就是在JDK 7时期已经规划的Jigsaw模块化功能,为何屡次发布都未能如约而至,前期可能是技术埋坑,后面遭到了以IBM和RedHat联合的等JCP委员抵抗,否决模块化提案,这种做法无疑是革了模块化支持本身就比较好的IBM的命,面对如此抵抗,Oracle丝毫没有退让,宁愿摒弃JSR独立研发,也要推行Jigsaw的发展,如果成真,想必Java也会面临如Python2和Python3的巨大不同,好在最终达成一直协议,Java还是那个完整的Java。JDK 9还提供了JS Shell、JLink、JHSDB等增强。JDK 9之后,Java也以更加敏捷的交付方式如期而至,每年的3月和9月发布一个版本,美6个月发布一个版本,每三年一个LTS版本,目的是避免众多功能交付在一个捆绑JDK的风险,并且提出了孵化器模块(Incubator)和预览特性(Preview)两个概念。也是在这个版本中CMS开始被废弃。
  • 2018年3月20日,Oracle公司发布JDK 10,主要对内部进行重构,统一源仓库,统一垃圾收集器接口,统一即时编译器接口(引入Graal几时编译器,这里也埋下一个伏笔),这些改进对于用户并不明显,但对后期的版本打下了良好的基础。
  • 2018年3月,同样发生了比较重要的一件事,Oracle正式宣布Java EE成为历史名称,曾经拥有着无数光辉的Java EE产品线(至今仍使用较为广泛的JDBC、JMS、Servlet等组件)被Oracle扫地出门,全部打包赠送给Eclipse基金会,并且不能使用Java商标,并且更名为Jakarta EE。
  • 2018年10月,自1996年以来每年一度的已经举办了22年的JavaOne大会,没有人预测到这也是最后一届,同年6月Java Mission Control的开发团队也被Oracle解散。
  • 2018年9月25日,JDK 11发布,这也是一个LTS(long-term support)版本,包含17个JEP,同时被引入的还有ZGC这样革命性的垃圾收集器,好比G1的诞生,比G1又更具神秘感。同时Oracle也调整了JDK的授权许可证,把以前的商业许可证授权给OpenJDK,官方宣布同时发布两个JDK,一个是Oracle OpenJDK,一个是OracleJDK,共享大部分源码近乎一致,个人均可免费使用,OpenJDK有半年的更新支持,OracleJDK商用必须付费,且支持三年的更新。因此Java收费纯属谣言,商业用户如果想使用最新的版本支持就必须付费。
  • 2019年3月20日,JDK 12发布,RedHat接手了OpenJDK 8和OpenJDk 11的管理和维护权。在JDK 12中包含了8个JEP,主要有Switch表达式和JMH测试套件,最引人注目的就是Shenandoah垃圾收集器,作为首个在JDK 7以后非Oracle开发的垃圾收集器,其目标与ZGC一致,这种竞争关系,立马得到了Oracle的抵制,在OracleJDK中剔除代码,因此Oracle JDK和OpenJDK的隔离性又如历史在JDK9中开始重演,至于后续Java的发展,以及迎接的挑战也是任重而道远,这取决于Oracle的产品线的定制和JCP的决策了。
  • 2019年在9月17日,JDK 13发布,这个版本主要通过改善Java SE平台和JDK的性能,稳定性和安全性来提高开发人员的生产力。共包含了5个JEPs和一个Unicode 12.1的支持总共6大主要新特性。
    • Support for Unicode 12.1 java.lang.Character支持Unicode Character12.1库,相比12.0库新增554个字符。
    • JEP 351: ZGC Uncommit Unused Memory ZGC增强了对未使用堆内存返还操作系统的能力,ZGC对堆内存的控制能力也从4TB提升至16TB,这个版本对ZGC进行了性能的增强。
    • JEP 350: Dynamic CDS Archiving 动态归档能力,HostSpotVM对AppCDS的一个增强。
    • JEP 354: Switch Expressions (Preview)switch表达式的引入,在后面的JDK 17中又继续对其功能进行了增强。
    • JEP 355: Text Blocks (Preview)增加了Java语言对文本块的支持,但这只是作为一个预览性质的功能,通过""" """来放置多行代码。
    • JEP 353: Reimplement the Legacy Socket API 重新实现了java.net.Socket and java.net.ServerSocket 类提供的APIs。
    • 2020年3月17日,JDK 14发布,这个版本主要是对JDK历史版本的一些增强,也引入了一些新增的功能.
    • JEP 359: Records (Preview) 新增了java.lang.Record类,Record是一种轻量级的class,可以看做是数据结构体。Java作为面向对象的语言诞生以来,在大型项目中的优势就体现出来了,但是也有一点不好,过于繁琐,一个POJO JavaBean必须包含get、set等方法,Record主要就是解决这类问题的,类似于Lombok的功能。
    • JEP 365: ZGC on Windows
    • JEP 364: ZGC on macOS
    • JEP 345: NUMA-Aware Memory Allocation for G1 G1的NUMA感知内存分配。这样可以提高使用非均匀内存体系结构(NUMA)的大型计算机的性能。
    • JEP 349: JFR Event Streaming 通过启用工具以异步方式订阅Java Flight Recorder事件,这可以对JVM进行更实时的监视。
    • JEP 363: Remove the Concurrent Mark and Sweep (CMS) Garbage Collector,CMS作为一款主打老年代的并发垃圾收集器,虽然历任JDK中都没有被设置为默认的垃圾收集器,但是在解决STW问题时也是有着举足轻重的地位的,从JDK 7开始筹备到JDK 9作为默认的G1垃圾收集器,一度被认为CMS的升级版,在次CMS也开始走下历史舞台。
    • JEP 366: Deprecate the ParallelScavenge + SerialOld GC Combination 随着每一代JDk中对垃圾收集器的升级以及新的垃圾收集器的引入,ParallelScavenge + SerialOld(Oracle指出很少有人使用)组合的GC也被弃用了,并在不久的将来被移除,完成了自己的历史使命。
  • 2020年9月15日,JDK 15发布,按照规划路线,JDK 14也停止更新,JDK 15虽然不是LTS版本,但也引入了一些开创性的功能和对早期版本功能的一些优化。
    • JEP 371: Hidden Classes,
    • JEP 378: Text Blocks,在JDK 13已经发布的二次预览版本,对于文本块的支持非常友好。
    • JEP 377: ZGC A Scalable Low-Latency Garbage Collector (Production),这个支持应该算是比较重要的,相比于JDK 11作为实验性质而引入的ZGC,在这两年中ZGC现在已准备好用于生产,不再标记为实验特征,可以直接使用 -XX:+UseZGC开启ZGC,不再需要XX:+UnlockExperimentalVMOptions额外的配置。需要注意的是在OracleJDK中使用的是ZGC,而在OpenJDK中使用的是Shenandoah GC,使用命令-XX:+UseShenandoahGC开启。
  • 2021年3月16日,JDK 16发布,这部分依旧是一些功能的优化升级。
    • JEP 389: Foreign Linker API (Incubator)提供静态键入的纯-java访问本机代码的API。
    • JEP 393: Foreign-Memory Access API (Third Incubator)提供了操作外部内存能力的API。
    • JEP 380: Unix domain sockets java.nio.channels, SocketChannel和ServerSocketChannel新增了对UNIX套接字的支持。
    • JEP 338: Vector API (Incubator) 提供孵化器模块的初始迭代,JDK.INCUBATOR.Vector,以表达在运行时可靠地编译的向量计算到支持的CPU架构上的最佳矢量硬件指令,从而实现对等效标量计算的卓越性能。这一点在JDK 17上持续增强。
    • JEP 376: ZGC Concurrent Stack Processing,ZGC的并发标记,不同于CMS的标记,ZGC的标记只有三个阶段,且每一个阶段的操作,无论是标记还是清理都是并发操作,大大降低了STW的时间,几乎零延时。这也算是Java在垃圾回收中比较开创性的变革。
    • JEP 387: Elastic Metaspace,JDK16对元数据区切分为更小的内存块,并将不再使用的内存快速返还给操作系统,对于频繁加载和卸载类的应用来说这一优化可以产生大量的空闲内存,提升整个JVM的性能。
    • JEP 397: Sealed Classes (Second Preview),对JDK内部方法提供强制的封装,在JDK 17依然进行了升级优化。
    • JEP 395: Records,从JDK 14作为预览版被提供在,这里已经升级为正式版功能。
    • JEP 392: Packaging Tool,新的打包工具,在JDK 14中通过JEP343被提案,在JDK 15中被孵化,在JDK 16中被提升为正式支持使用,通过jpackage命令支持native可执行程序打包。
  • 2021年9月14日,JDK17发布,这也是在JDK 11之后的下一个LTS版本,JDK 17也是Java六个月发布节奏下的最新的长期支持(LTS)发布,主打安全、性能、稳定为特性,并且官方计划支持到2029年9月。在这个版本中包含了14个JEPs更新。
    • JEP 356: Enhanced Pseudo-Random Number Generator 伪随机数增强器
    • JEP 391: macOS AArch64 Port 新平台的支持
    • JEP 410: Remove the Experimental AOT and JIT Compiler 移除了实验性质的AOT和JIT编译器
    • JEP 411: Deprecate the Security Manager for Removal 弃用安全管理器
    • JEP 406: Pattern Matching for switch (Preview) switch表达式,这可能是一个让switch翻身的功能。
    • JEP 412: Foreign Function and Memory API (Incubator) 外函数和内存相关的API,在JDK 14和JDK 15中引入的孵化API,使Java程序能够与Java运行时之外的代码和数据互操作,通过有效地调用外部函数(即,JVM之外的代码),并通过安全访问异物,这些API使Java程序能够调用本机库并进入本机数据而不提供Java本机接口(JNI)的脆性和复杂性。
    • JEP 414: Vector API (Second Incubator)允许表达在运行时可靠地编译的向量计算以支持的CPU架构上的优化矢量指令,从而实现优于等效标量计算的性能

​更多JEP:JEPs 更多JDK版本变更信息:https://www.oracle.com/java/technologies/javase/jdk-relnotes-index.html

JVM家族

  • JDK 1.0,1996年引入Sun Classic VM,虚拟机鼻祖,世界上第一款商用Java虚拟机。
  • JDK 1.2,Solaris平台发布Exact VM,已经初具现代高性能虚拟机雏形,出现热点探测、即时编译等。但是生命周期短暂,这一时期并存Sun Classic VM、Exact VM、HotSpot VM,可通过命令切换。
  • JDK 1.3,HotSpot VM成为默认选择,Exact VM依然为备用选择。
  • JDK 1.4,Classic VM才完全退出商用虚拟机的历史舞台,与Exact VM一起进入了Sun Labs Research VM之中。HotSpot继承了Sun公司两款虚拟机的优点,自此之后HotSpot VM也一直成为OracleJDK和OpenJDK中默认的虚拟机。得益于Sun和Oracle两大巨头在不同时期的影响力,HotSpot也成为了使用最为广泛的虚拟机。
  • Mobile/Embedded VM,这是一款面向JavaME产品线的虚拟机。主打移动端和嵌入式设备市场。只留下了客户端编译器(C1),去掉了服务端编译器(C2);只保留Serial/Serial Old垃圾收集器,去掉了其他收集器等,是一款小家碧玉型产品。
  • BEA JRockit,号称最快的虚拟机,天下武功唯快不破,当然也有可能每一家都这么宣传,后来BEA公司也被Oracle收购,由于其设计架构与HotSpot大相径庭,因此只有少部分监控相关的优势被吸纳进去,天下武功,九九归一。
  • IBM J9,内部名称曾定义为IT4J(IBM Technology for Java Virtual Machine),太长的名字,因此也看出名字的重要性,至今仍然活跃,与之对应的是可以更好的兼容在IBM的小型机运行平台,模块化也是其显著的优势和特点,因此可以按需启用,消耗较小的资源,提供更大的优势。
  • BEA LiquidVM/Azul VM 相比于大名鼎鼎的HotSpot、JRockit、J9等通用型的多平台兼容的JVM,还有一种与特定平台,硬件绑定的专有化虚拟机,BEA的Liquid VM不需要依托于其他操作系统,本身就已经系统化,直接运行在自家Hypervisor系统上。Azul VM是Azul Systems公司在HotSpot基础上进行大量改进,运行于自家专有硬件Vega上的虚拟机,可以管理大量的CPU和内存,使用的是有名的PGC和C4收集器。随着业务线的调整,2010年,Azul转型软件业务,把全部精力投入到Zing(以软件的方式达到接近Vega系统的虚拟机)的研发中,而HotSpot在JDK 11和JDK 12中的ZGC和Shenandoah也才达到同等目标,JVM的发展竞争也渐激烈。

除了上面提到的使用广泛或者名气比较大的商用虚拟机外,也存在一些对整个Java虚拟机产生深渊影响的产品。

  • Apache Harmony,Harmony中的DRLVM,这是一个兼容JDK 5和JDK 6的平台,可以运行Eclipse、Tomcat、Maven等程序,一直没有获取TCK授权,在Oracle收购Sun之后,Apache一度退出JCP组织,这也是Java社区历史上最为割裂的一件事。直到OpenJDK的诞生,Harmony的优势被极大的抵消,项目的最大参与者IBM也宣布退出,虽然Harmony从诞生起就没有大规模商用,但是Harmony的代码被吸收进IBM的JDK 7和Google Android SDK,这也对Android的发展产生了深远影响,似乎某种程度圆了Java诞生之初Sun就设定的路线,主打移动端的嵌入式设备路线。
  • Microsoft JVM,可曾想到微软当初也设计过自己的Java虚拟机,但被Sun以侵权告退,并且终止了Java虚拟机的研发,也许正因如此才诞生了.NET平台,能让你强大的永远是你的对手,微软也在一次证明了自己的强大。

Java虚拟机的发展也进入到一种百花齐放,百家争鸣的局面,除了大规模的服务器级别的虚拟机外,也存在一些小型的虚拟机平台。

  • KVM,Android、IOS等智能手机操作系统出现前广泛使用的手机平台。
  • JCVM,Java虚拟机的一个子集,如智能卡,SIM卡,信用卡等,通常用作加密模块。
  • Squawk VM,由Sun开发曾用于Java Card的嵌入式虚拟机实现。
  • JavaInJava,Sun公司开发的一个实验性质的虚拟机,通过元循环证明一门语言可以自举,通过Java来实现Java语言的运行环境,没有编译器,通过解释模式来运行。
  • Maxine VM,一个类似于JavaInJava的产品,效率跟高,接近于HotSpot。
  • Jikes RVM,由IBM研发的一个实验性质的项目,类似于JavaInJava。
  • http://IKVM.NET,基于.NET平台实现的Java虚拟机,通过Mono实现了一定的跨平台能力。

Java经过了这么多年的发展,经历过公司更替,经历过组织割裂,沧海桑田,都使其走了过来,在互联网快速发展的今天,这是一个最好的时代,也是一个最坏的时代,挑战与机遇并存,Oracle公司推出的Graal VM也被官方称为Universal VM,这也是未来最有可能替代HotSpot VM的产品,也将承担起Java迎接更大的挑战。

GC算法

Java语言最大的优势就是垃圾自动回收,不需要开发者去关心内存管理的事情,可以专注于业务逻辑开发。内存自动回收的核心GC算法,每一种GC都是基于不同的算法理论来实现的,因此算法对GC的性能、回收等都起着至关重要的作用。GC算法之间没有好坏之分,只有适合或不适合,每一个GC算法也不是一蹴而就的,是经过无数的迭代改进诞生的。

  • 引用计数算法,这个算法的原理很简单,在对象中添加一个引用计数器,每当有一个地方引用它时,计数器值就加一;当引用失效时,计数器值就减一;任何时刻计数器为零的对象就是不可能再被使用的。但是在主流的垃圾收集器中都没有使用过这种算法,当出现两个对象之间互相引用,但是这两个对象又没有实际被使用的场景,会导致引用计数都不能清零,成为无法回收的垃圾。
  • 可达性分析算法,这是主流虚拟机都在使用的一种垃圾回收算法,通过一些GC Roots来向下搜索,形成一个个引用链,如果某个对象没有到达GC Roots的引用链,那么就判定这个对象可回收,也就解决了引用计数算法里面判定的问题。因此也定义了一些常见的GC Roots。
    • 虚拟机栈(栈帧中的本地变量表)中引用的对象
    • 本地方法栈中 JNI(即一般说的 Native 方法)引用的对象
    • 方法区中类静态属性引用的对象
    • 方法区中常量引用的对象
    • 标记-清除算法,Mark-Sweep,算法分为标记清除两阶段,首先对需要回收的对象进行统一标记,在标记结束后,统一回收,也可以统一标记存活对象,统一回收未标记对象。这个算法是最基础的,但是也有很多问题,比如大量的标记和清除,会导致两个阶段的效率都不同程度的降低,其次是标记清除后空间的不连续性,不利于大对象的分布,并由此触发下一次GC。
  • 标记-复制算法,Semispace-Copying,使用的是半区复制的思路,与清除所不一样的是,复制算法,将新生代既定内存区域按照1:1分为两部分,把存活对象迁移至其中一个半区,然后清理已使用过的空间,这种算法复制过程通过指针移动来实现,因此带来的开销是内存空间开销,会消耗大量的内存。从某种程度上标记-复制解决了标记-清理在处理大量可回收对象时遇到的效率问题,但是空间消耗是其要面对的另一个问题。因此对于这种情况,考虑到大部分场景的对象都是朝生夕灭,存活周期较短,将新生代按照比例1:8划分为Survivor:Eden区,Survivor又划分为From和To两个区域,每次分配内存只使用Eden和其中一块Survivor(From)。发生垃圾收集时,将Eden和Survivor(From)中仍然存活的对象一次性复制到另外一块Survivor(To)空间上,然后直接清理掉Eden和已用过的那块Survivor(From)空间,清空后存在一个From->To的转变,出现From和To的角色互换,总是保证在发生GC之前保证To区域是空闲的。当Survivor不足以承担一次MinorGC时就需要通过分配担保(Handle Promotion)机制来将一些对象迁移到老年代。
  • 标记-整理算法,Mark-Compact,相当于是标记-清除,标记-复制算法的优势整合,标记阶段通标记-清除算法的标记阶段一致,但是在标记结束后不直接清理,而是让存活对象向内存空间另一端移动,这点类似于复制的处理。标记-整理算法是建立在是否移动回收后的存活对象这个风险点之上的,移动对象会加大延迟,STW现象更为显著,但是带来的好处是移动对象后整片的内存区域可以提高吞吐量,因此在HotSpot VM中关注吞吐量的Parallel Scavenge GC是基于标记-整理算法实现的,关注延迟的CMS是基于标记-清除算法实现的。

垃圾收集器

在《Java虚拟机规范》中并没有规定垃圾应该如何回收,只是提出了这样一种理论,因此不同的厂商也有不同的实现方式,在众多的JVM中也存在着形形色色的GC,在此主要说的也是HotSpot VM中出现过的垃圾收集器。在JDK 7之前也就是G1出现之前,垃圾收集器是分代收集的按照新生代和老年代有不同的GC来进行内存回收,在G1之后出现的GC基本都是面向全堆栈回收,因此也可以对GC做不同的分类。

  • 新生代GC
    • Serial GC,这是最基础也是历史最悠久的收集器,在JDK 1.3.1之前,HotSpot中新生代唯一的收集器选择,在进行收集的时候会暂停所有工作线程直到收集结束,由于其单线程模式,也导致了STW时间过长,用户应用处于长时间假死状态,非常影响用户体验。但也是所有收集器中内存资源消耗最少的,在单核或者核心较小的服务器环境,也能体现出其特有的优势。在客户端模式下是一个不错的选择。
    • ParNew GC,实质上是Serial的多线程版本,除了在收集垃圾采用多线程并行收集外,其他地方与Serial并无较多差异,是不少在服务端模式下运行的虚拟机的首选新生代收集器。除了Serial,只有ParNew可以用CMS配合使用。
    • Parallel Scavenge GC,基于标记复制算法实现的收集器,其目标在于达到一个可控制的吞吐量(处理器用于运行用户代码时间与处理器总消耗时间的比值),良好的响应速度可以提高用户体验,高吞吐量可以最高效率的利用处理器资源,尽快完成运算任务,适合运算多而交互少的分析任务。
  • 老年代GC
    • Serial Old GC,相对于Serial收集器,Serial Old专用于处理老年代GC,使用标记整理算法,主要也是在客户端模式下使用,如果在服务端模式下,在JDK 1.5之前与Parallel Scavenge配合使用,或者作为CMS失败后的预案,在Concurrent Mode Failure时使用。
    • Parallel Old GC,PArallel Old是Parallel Scavenge的老年代版本,基于标记整理算法,支持多线程并发收集,在JDK 6提供,在Parallel Old出现之前,Parallel Scvenge只能和Serial Old搭配使用,比较尴尬,CMS无法与之配合,而Serial Old又不适合服务端模式,因此在此之前使用较多的也是ParNew + CMS组合。在吞吐量优先的场景中,PS + PO的组合实至名归。
    • CMS GC,JDK 5推出的一个划时代意义的收集器,首款真正做到了并发收集的垃圾收集器,实现了垃圾收集线程与用户线程同时运行。无法与Parallel Scavenge配合工作,因此CMS的出现也巩固了ParNew的地位。CMS使用了标记清除算法,一般分为四个阶段,初始标记->并发标记->重新标记->并发清除,由于初始标记和并发标记的存在,大大减小了STW的时间,但是CMS无法很好的处理浮动垃圾(并发清理阶段,用户线程产生的垃圾,无法被及时清除,保留在下一个GC阶段),为了处理浮动垃圾,CMS需要老年代预留一部分内存,通过设定的阈值来确定GC的时机。CMS也是在一次次不完美的过程中尝试完美。
    • G1 GC,G1在JDK 7中进入Experimental状态,在JDK 8中日趋成熟,在JDK 9中开始正式成为HotSpot的默认GC,替代了服务端主打的PS + PO组合收集器,此后也在Oracle官方被称为全功能垃圾收集器(Full-Featured Garbage Collector),主要面向服务端。G1是垃圾收集器发展史上具有里程碑式的成果,开创了面向局部收集的设计,每一个Region都可以扮演新生代的Eden、Survivor或者老年代空间。
  • 低延迟垃圾收集器(衡量指标:内存占用、吞吐量、延迟)
    • Shenandoah GC,第一款不是Sun或者Oracle设计的GC,甚至在OracleJDK 12中被明确拒绝的GC,因此只能存在与OpenJDK中,开源比收费的功能反而更多。相比于G1更像是G1的继承者,多线程Full GC的支持、类似于Region的设计有着优于G1的更多设计,都是为了降低延迟。
    • Z GC,JDK 11开始加入的GC,与Shenandoah目标类似,但设计思路又大有不同,Shenandoah更类似于G1的延伸,ZGC更像Azul公司PGC和C4的复刻。ZGC使用染色指针技术来作为其标志性的并发整理算法实现,支持更大的内存管理和更高效的垃圾收集。
    • Epsilon GC,JDK 11中依然出现了一个实验性质的GC,不能够进行垃圾收集的垃圾收集器,相比于G1、Shenandoah、ZGC复杂的垃圾收集算法和设计实现,Epsilon有点反其道而行之的意味,事实上一个GC的功能不仅仅是回收这个动作,还要负责堆管理布局,对象分配以及与解释器的协同工作等,如果在某些不需要回收仍然可以正常运行的Java虚拟机系统中Epsilon似乎是一种更好的选择。解决问题的方式固然重要,处理问题的思路也很重要。

之所以存在这么多的垃圾收集器,也就说明了没有一个完美的GC来适应所有场景,因此面对不同的场景,分代收集和全堆栈收集也给JVM调优指明了思路,了解不同GC的设计特点,才能更好的选择。

无论是JDK、GC、GC算法哪一个时间线,都可以无限的延伸下去,不是三言两语可以解释清楚的。这里也仅仅介绍梳理了一下发展过程,在JVM不断的演变之中,一些传统的认知也在被更替,比如所谓的Java中对象是在堆内存分配,随着逃逸分析,栈上分配,标量替换等更先进的技术发展出以后,Java中对象的分配也不是一成不变的,是按照具体的情况来分配的。

结语

本文简要介绍了Java的基本概念、特点、应用领域和发展历程以及JDK、JVM、GC算法的发展介绍。作为一种面向对象的、跨平台的编程语言,Java在软件开发领域扮演着重要角色。无论是开发企业级应用、移动应用,还是嵌入式系统和大数据处理等领域,Java都能够提供强大的支持和丰富的开发资源。

此文即将收录到本站系列教程——Java入门教程中,您可以收藏本站持续关注

正文到此结束
评论插件初始化中...
Loading...