【Java自学】一、Java语言概述与开发环境
1.1 Java语言的发展简史
Java 语言的诞生具有一定的戏剧性,它并不是经过精心策划、制作,最后产生的划时代产品,从某个角度来看, Java 语言的诞生完全是一种误会。
1990 年年末, Sun 公司预料嵌入式系统将在未来家用电器领域大显身手。于是Sun 公司成立了一个由James Gosling 领导的" Green 计划",准备为下一代智能家电( 如电视机、微波炉、电话)编写一个通用控制系统。
该团队最初考虑使用C++语言,但是很多成员包括Sun 的首席科学家Bill Joy ,发现C++和可用的API 在某些方面存在很大问题。而且工作小组使用的是嵌入式平台,可用的系统资源极其有限。并且很多成员都发现C++太复杂,以致很多开发者经常错误使用。而且C++缺少垃圾回收系统、可移植性、分布式和多线程等功能。
根据可用的资金, Bi1l Joy 决定开发一种新语言,他提议在C++的基础上,开发一种面向对象的环境。于是, Gosling 试图通过修改和扩展C++的功能来满足这个要求,但是后来他放弃了。他决定创造一种全新的语言: Oak 。
到了1992 年的夏天, Green 计划己经完成了新平台的部分功能,包括Green 操作系统、Oak 的程序设计语言、类库等。同年11 月, Green 计划被转化成" FirstPerson 有限公司", 一个Sun 公司的全资子公司。
FirstPerson 团队致力于创建一种高度互动的设备。当时代华纳公司发布了一个关于电视机顶盒的征求提议书时, FirstPerson 改变了他们的目标,作为对征求提议书的响应,提出了一个机顶盒平台的提议。但有线电视业界觉得FirstPerson 的平台给予用户过多的控制权,因此FirstPerson 的投标败给了SGI 。同时,与3DO 公司的另外一笔关于机顶盒的交易也没有成功。此时,可怜的Green 项目几乎接近夭折,甚至Green 项目组的一半成员也被调到了其他项目组。
正如中国古代的寓言所言: 塞翁失马,焉知非福?如果Green 项目在机顶盒平台投标成功,也许就不会诞生Java 这门伟大的语言了。
1994 年夏天,互联网和浏览器的出现不仅给广大互联网的用户带来了福音,也给Oak 语言带来了新的生机。Gosling 立即意识到,这是一个机会,于是对Oak 进行了小规模的改造,到了1994 年秋,小组中的Naughton 和Jonathan Payne 完成了第一个Java 语言的网页浏览器: WebRunner 0 Sun 公司实验室主任Bert Sutherland 和技术总监Eric Schmidt 观看了该浏览器的演示,对该浏览器的效果给予了高度评价。当时Oak 这个商标己被别人注册,于是只得将Oak 更名为Java 。
Sun 公司在1995 年年初发布了Java 语言, Sun 公司直接把Java 放到互联网上,免费给大家使用。甚至连源代码也不保密,也放在互联网上向所有人公开。
几个月后,让所有人都大吃一惊的事情发生了: Java 成了互联网上最热门的宝贝。竟然有10 万多人次访问了Sun 公司的网页,下载了Java 语言。然后,互联网上立即就有数不清的Java 小程序(也就是Applet) ,演示着各种小动画、小游戏等。
Java 语言终于扬眉吐气了,成为了一种广为人知的编程语言。
在Java 语言出现之前,互联网的网页实质上就像是一张纸,不会有任何动态的内容。有了Java 语言之后,浏览器的功能被扩大了, Java 程序可以直接在浏览器里运行,可以直接与远程服务器交互:用Java 语言编程,可以在互联网上像传送电子邮件一样方便地传送程序文件!
1995 年, Sun 虽然推出了Java ,但这只是一种语言,如果想开发复杂的应用程序,必须要有一个强大的开发类库。因此, Sun 在1996 年年初发布了JDK 1. 0 。这个版本包括两部分:运行环境(即JRE)和开发环境(即JDK) 。运行环境包括核心API 、集成API 、用户界面API 、发布技术、Java 虚拟机(NM)5 个部分;开发环境包括编译Java 程序的编译器(ep javac 命令) 。
接着, Sun 在1997 年2 月18 日发布了JDK 1.1 0 JDK 1.1增加了JIT (即时编译)编译器。JIT 和传统的编译器不同,传统的编译器是编译一条,运行完后将其扔掉:而且JIT会将经常用到的指令保存在内存中,当下次调用时就不需要重新编译了,通过这种方式让JDK 在效率上有了较大提升。但一直以来, Java 主要的应用就是网页上的Applet 以及一些移动设备。到了1996 年年底, Flash面世了,这是一种更加简单的动画设计软件:使用Flash 几乎无须任何编程语言知识,就可以做出丰富多彩的动画。随后Flash 增加了ActionScript 编程脚本, Flash 逐渐蚕食了Java 在网页上的应用。
从1995 年Java 的诞生到1998 年年底, Java 语言虽然成为了互联网上广泛使用的编程语言,但它并没有找到一个准确的定位,也没有找到它必须存在的理由: Java 语言可以编写Applet ,而Flash 一样可以做到,而且更快,开发成本更低。
直到1998 年12 月, Sun 发布了Java 历史上最重要的JDK 版本: JDK 1. 2 ,伴随JDK 1.2 一同发布的还有JSP/Servlet 、EJB 等规范,并将Java 分成了J2EE 、J2SE 和J2ME 三个版本。
- J2ME: 主要用于控制移动设备和信息家电等有限存储的设备。
- J2SE: 整个Java 技术的核心和基础,它是J2ME 和J2EE 编程的基础,也是这本书主要介绍的内容。
- J2EE: Java 技术中应用最广泛的部分, J2EE 提供了企业应用开发相关的完整解决方案。
这标志着Java 己经吹响了向企业、桌面和移动三个领域进军的号角,标志着Java 己经进入Java 2时代,这个时期也是Java 飞速发展的时期。
在Java 2 中, Java 发生了很多革命性的变化,而这些革命性的变化一直沿用到现在,对Java 的发展形成了深远的影响。直到今天还经常看到J2EE 、J21在E 等名称。
不仅如此, JDK 1.2 还把它的API 分成了三大类。
- 核心API: 由Sun 公司制定的基本的API ,所有的Java 平台都应该提供。这就是平常所说的Java核心类库。
- 可选API: 这是Sun 为JDK 提供的扩充API,这些API 因乎台的不同而不同。
- 特殊API: 用于满足特殊要求的API 。如用于JCA 和JCE 的第三方加密类库。
2002 年2 月, Sun 发布了JDK 历史上最为成熟的版本: JDK 1 .4 。此时由于Compaq 、Fujitsu , SAS 、Symbian 、ffiM 等公司的参与,使JDK 1 .4成为发展最快的一个JDK 版本。JDK 1 .4己经可以使用Java实现大多数的应用了。
在此期间, Java 语言在企业应用领域大放异彩,涌现出大量基于Java 语言的开源框架: Structs、WebWork 、Hibemate 、Spring 等:大量企业应用服务器也开始涌现: WebLogic 、WebSphere 、JBoss 等,这些都标志着Java 语言进入了飞速发展时期。
2004 年10 月, Sun 发布了万众期待的JDK 1. 5 ,同时, Sun 将JDK 1.5 改名为Java SE 5.0, J2EE 、J2ME 也相应地改名为Java EE 和Java ME。 JDK 1. 5 增加了诸如泛型、增强的for 语句、可变数量的形参、注释(Annotations)、自动拆箱和装箱等功能;同时,也发布了新的企业级平台规范,如通过注释等新特性来简化EJB 的复杂性,并推出了EJB 3.0 规范。还推出了自己的MVC 框架规范: JSF , JSF 规范类似于ASP.NET 的服务器端控件,通过它可以快速地构建复杂的JSP 界面。
2006 年12 月, Sun 公司发布了JDK 1.6 (也被称为Java SE 6) 。一直以来, Sun 公司维持着大约2年发布一次JDK 新版本的习惯。但在2 009 年4 月20 日, Oracle 宣布将以每股9.5 美元的价格收购Sun ,该交易的总价值约为74亿美元。而Oracle 通过收购Sun 公司获得了两项软件资产: Java 和So laris 。于是曾经代表一个时代的公司: Sun 终于被"雨打风吹"去, 66 江湖"上再也没有了Sun 的身影。多年以后,在新一辈的程序员心中可能会遗忘曾经的Sun 公司,但老一辈的程序员们将永久地怀念Sun
公司的传奇。
Sun 倒下了,不过Java 的大旗依然猎猎作响。2007 年11 月, Google 宣布推出一款基于Linux 平台的开源于机操作系统: Android 。 Android 的出现顺应了即将出现的移动互联网潮流,而且Android 系统的用户体验非常好,因此迅速成为手机操作系统的中坚力量。Android 平台使用了Dalvik 虚拟机来运行. dex 文件, Dalvik 虚拟机的作用类似于NM 虚拟机,只是它并未遵守NM 规范而己。Android 使用Java 语言来开发应用程序,这也给了Java 语言一个新的机会。在过去的岁月中, Java 语言作为服务器端编程语言,己经取得了极大的成功:而Android 平台的流行,则让Java 语言获得了在客户端程序上大展拳脚的机会。
2011 年7 月28 日, Oracle 公司终于"如约"发布了Java SE 7一一这次版本开级经过了将近5 年时间。Java SE 7 也是Oracle 发布的第一个Java 版本,引入了二进制整数、支持字符串的switch 语句、菱形语法、多异常捕捉、自动关闭资源的位yl吾句等新特性。
2014 年3 月18 日, Oracle 公司发布了Java SE 8 ,这次版本升级为Java 带来了全新的Lambda 表达式、流式编程等大量新特性,这些新特性使得Java 变得更加强大。
2017 年9 月22 日, Oracle 公司发布了Java SE 9 ,这次版本升级强化了Java 的模块化系统,让庞大的Java 语言更轻量化,而且采用了更高效、更智能的Gl 垃圾回收器,并在核心类库上进行了大量更新,可以进一步简化编程:但对语法本身更新并不多(毕竟Java 语言己经足够成熟) 。
1.2 Java 程序运行机制
Java 语言是一种特殊的高级语言,它既具有解释型语言的特征,也具有编译型语言的特征,因为Java 程序要经过先编译,后解释两个步骤。
1.2.1 高级语言的运行机制
计算机高级语言按程序的执行方式可以分为编译型和解释型两种。
编译型语言是指使用专门的编译器,针对特定平台(操作系统)将某种高级语言源代码一次性"翻译"成可被该平台硬件执行的机器码(包括机器指令和操作数),并包装成该平台所能识别的可执行性程序的格式,这个转换过程称为编译CCompile ) 。编译生成的可执行性程序可以脱离开发环境,在特定的平台上独立运行。
有些程序编译结束后,还可能需要对其他编译好的目标代码进行链接,即组装两个以上的目标代码模块生成最终的可执行性程序,通过这种方式实现低层次的代码复用。
因为编译型语言是一次性地编译成机器码,所以可以脱离开发环境独立运行,而且通常运行效率较高; 但因为编译型语言的程序被编译成特定平台上的机器码,因此编译生成的可执行性程序通常无法移植到其他平台上运行;如果需要移植,则必须将源代码复制到特定平台上,针对特定平台进行修改,至少也需要采用特定平台上的编译器重新编译。
现有的C 、C++ 、Objective-C 、Swift、Kotlin 等高级语言都属于编译型语言。
解释型语言是指使用专门的解释器对源程序逐行解释成特定平台的机器码并立即执行的语言。解释型语言通常不会进行整体性的编译和链接处理,解释型语言相当于把编译型语言中的编译和解释过程混合到一起同时完成。
可以认为: 每次执行解释型语言的程序都需要进行一次编译,因此解释型语言的程序运行效率通常较低,而且不能脱离解释器独立运行。但解释型语言有一个优势: 跨平台比较容易,只需提供特定平台的解释器即可,每个特定平台上的解释器负责将源程序解释成特定平台的机器指令即可。解释型语言可以方便地实现源程序级的移植,但这是以牺牲程序执行效率为代价的。
现有的JavaScript 、Ruby 、Python 等语言都属于解释型语言。
除此之外,还有一种伪编译型语言,如Visual Basic ,它属于半编译型语言,并不是真正的编译型语言。它首先被编译成P-代码,并将解释引擎封装在可执行性程序内,当运行程序时, P-代码会被解析成真正的二进制代码。表面上看起来, Visual Basic 可以编译生成可执行的EXE 文件,而且这个EXE文件也可以脱离开发环境,在特定平台上运行,非常像编译型语言。实际上,在这个EXE 文件中,既有程序的启动代码, 也有链接解释程序的代码,而这部分代码负责启动Visual Basic 解释程序,再对Visual Basic 代码进行解释并执行。
1.2.2 Java 程序的运行机制和JVM
Java 语言比较特殊,由Java 语言编写的程序需要经过编译步骤,但这个编译步骤并不会生成特定平台的机器码,而是生成一种与平台无关的字节码(也就是*.class 文件) 。当然,这种字节码不是可执行的,必须使用Java 解释器来解释执行。因此可以认为: Java 语言既是编译型语言,也是解释型语言。或者说, Java 语言既不是纯粹的编译型语言,也不是纯粹的解释型语言。Java 程序的执行过程必须经过先编译、后解释两个步骤,如下图所示。

Java 语言里负责解释执行字节码文件的是Java 虚拟机,即JVM ( Java Virtual Machine)。JVM 是可运行Java 字节码文件的虚拟计算机。所有平台上的JVM向编译器提供相同的编程接口, 而编译器只需要面向虚拟机,生成虚拟机能理解的代码, 然后由虚拟机来解释执行。在一些虚拟机的实现中,还会将虚拟机代码转换成特定系统的机器码执行, 从而提高执行效率。
当使用Java 编译器编译Java 程序时,生成的是与平台无关的字节码, 这些字节码不面向任何具体平台,只面向JVM 。不同平台上的JVM 都是不同的,但它们都提供了相同的接口。JVM 是Java 程序跨平台的关键部分,只要为不同平台实现了相应的虚拟机,编译后的Java 字节码就可以在该平台上运行。显然,相同的字节码程序需要在不同的平台上运行,这几乎是"不可能的",只有通过中间的转换器才可以实现, JVM就是这个转换器。
JVM是一个抽象的计算机, 和实际的计算机一样,它具有指令集并使用不同的存储区域。它负责执行指令,还要管理数据、内存和寄存器。
Oracle 公司制定的Java 虚拟机规范在技术上规定了JVM 的统一标准,具体定义了JVM 的如下细节:
- 指令集
- 寄存器
- 类文件的格式
- 栈
- 垃圾回收堆
- 存储区
Oracle 公司制定这些规范的目的是为了提供统一的标准,最终实现Java 程序的平台无关性。
1.3 开发Java 的准备
在开发Java 程序之前,必须先完成一些准备工作,也就是在计算机上安装并配置Java 开发环境,开发Java 程序需要安装和配置JDK 。
1.3.1 下载和安装Java 9 的JDK
JDK 的全称是Java SE Development Kit ,即Java 标准版开发包,是Oracle 提供的一套用于开发Java应用程序的开发包,它提供了编译、运行Java 程序所需的各种工具和资源,包括Java 编译器、Java 运行时环境,以及常用的Java 类库等。
这里又涉及一个概念: Java 运行时环境,它的全称是Java Runtime Environment ,因此也被称为JRE ,它是运行Java 程序的必需条件。一般而言,如果只是运行Java 程序,可以只安装JRE ,无须安装JDK 。
Oracle 把Java 分为Java SE 、Java EE 和Java ME 三个部分,而且为Java SE 和Java EE 分别提供了JDK 和Java EE SDK (Software Development Kit) 两个开发包,如果只需要学习Java SE 的编程知识,则可以下载标准的JDK; 如果学完Java SE 之后,还需要继续学习Java EE 相关内容,也可以选择下载Java EE SDK , 有一个Java EE SDK 版本里己经包含了最新版的JDK , 安装Java EE SDK 就包含了JDK 。
登录http: //www.oracle.com/technetwork/java/javase/downloads/index.html ,下载Java SE Development Kit 的最新版本。(安装过程略过)
安装完成后,可以在JDK 安装路径下看到如下的文件路径。
- bin: 该路径下存放了JDK 的各种工具命令,常用的Javac 、Java 等命令就放在该路径下。
- conf: 该路径下存放了JDK 的相关配置文件。
- include: 存放一些平台特定的头文件。
- jmods: 该目录下存放了JDK 的各种模块。
- legal: 该目录下包含了JDK 各模块的授权文档。
- lib: 该路径下存放的是JDK 工具的一些补充JAR 包。比如src .zip 文件中保存了Java 的源代码。
- README和COPYRIGHT 等说明性文档。
模块化系统是JDK9 的重大更新,随着Java 语言的功能越来越强大, Java 语言也越来越庞大。很多时候, 一个基于Java 的软件并不会用到Java 的全部功能,因此该软件也不需要加载全部的Java 功能,而模块化系统则允许发布Java 软件系统时根据需要只加载必要的模块。
为此, JDK 专门引入了一种新的JMOD 格式,它近似于JAR 格式,但JMOD 格式更强大, 它可以包含本地代码和配置文件.
1.3.2 设置PATH 环境变量
前面已经介绍过了,编译和运行Java 程序必须经过两个步骤。
- 将源文件编译成字节码。
- 解释执行平台无关的字节码程序.
1.4 Java 程序的基本规则
前面己经编写了Java 学习之旅的第一个程序,下面对这个简单的Java 程序进行一些解释,解释Java程序必须满足的基本规则。
1.4.1 Java 程序的组织形式
Java 程序是一种纯粹的面向对象的程序设计语言,因此Java 程序必须以类(class) 的形式存在,类( class ) 是Java 程序的最小程序单位。Java 程序不允许可执行性语句、方法等成分独立存在,所有的程序部分都必须放在类定义里。
1.4.2 Java 源文件的命名规则
Java 程序源文件的命名不是随意的, Java 文件的命名必须满足如下规则。
- Java 程序源文件的扩展名必须是.java , 不能是其他文件扩展名。
- 在通常情况下, Java 程序源文件的主文件名可以是任意的。但有一种情况例外:如果Java 程序源代码里定义了一个public 类, 则该源文件的主文件名必须与该public 类(也就是该类定义使用了public 关键字修饰〉的类名相同。
由于Java 程序源文件的文件名必须与public 类的类名相同,因此, 一个Java 源文件里最多只能定义一个public 类。
虽然Java 源文件里没有包含public 类定义时,这个源文件的文件名可以是随意的,但推荐让Java源文件的主文件名与类名相同, 这可以提供更好的可读性。通常有如下建议:
- 一个Java 源文件只定义一个类,不同的类使用不同的源文件定义。
- 让Java 源文件的主文件名与该源文件中定义的public 类同名。
1.5 JDK 9 新增的jshell 工具
JDK9 工具的一大改进就是提供了jshe11 工具, 它是一个REPL (Read-Eval-Print Loop ) 工具, 该工具是一个交互式的命令行界面,可用于执行Java 语言的变量声明、语句和表达式,而且可以立即看到执行结果。因此,我们可以使用该工具来快速学习Java 或测试Java 的新API 。
对于一个立志学习编程(不仅是Java) 的学习者而言, 一定要记住:看再好的书也不能让自己真正掌握编程(即使如《疯狂Java 讲义》也不能) ! 书只能负责指导,但最终一定需要读者自己动手。即使是一个有经验的开发者,遇到新功能时也会需要通过代码测试。
在没有jshe11 时,开发者想要测试某个新功能或新API, 通常要先打开IDE 工具(可能要花l 分钟),然后新建一个测试项目,再新建一个类,最后才可以开始写代码来测试新功能或新API 。这真要命啊!而jshe11 的出现解决了这个痛点。
开发者直接在jshe11 界面中输入要测试的功能或代码, jshell 会立刻反馈执行结果,非常方便。
启动jshell 非常简单, 只要在命令行窗口输入jshe11 命令,即可进入jshell 交互模式。
进入jshell 交互模式后,可执行/help 来查看帮助信息,也可执行/exit 退出jshell.如图1.1 3所示。执行你希望测试的Java 代码,比如执行System.out.println("Hello World!") ,此处不要求以分号结尾。
从图l.1 3 可以看出,除/help 、/exit 之外, jshell还有如下常用命令。
- /list: 列出用户输入的所有源代码。
- /edit: 编辑用户输入的第几条源代码。比如/edit 2 表示编辑用户输入的第2 条源代码。jshell 会启动一个文本编辑界面让用户来编辑第2 条源代码。
- /drop: 删除用户输入的第几条源代码。
- /save: 保存用户输入的源代码。
- /vars : 列出用户定义的所有变量。
- /methods: 列出用户定义的全部方法。
- /types: 列出用户定义的全部类型。
1.6 Java 9 的Gl 垃圾回收器
传统的C/C++等编程语言, 需要程序员负责回收己经分配的内存。显式进行垃圾回收是一件比较困难的事情,因为程序员并不总是知道内存应该何时被释放。如果一些分配出去的内存得不到及时回收,就会引起系统运行速度下降,甚至导致程序瘫痪,这种现象被称为内存泄漏。总体而言,显式进行垃圾回收主要有如下两个缺点。
- 程序忘记及时回收无用内存,从而导致内存泄漏,降低系统性能。
- 程序错误地回收程序核心类库的内存,从而导致系统崩溃。
与C/C++程序不同, Java 语言不需要程序员直接控制内存回收, Java 程序的内存分配和回收都是由JRE 在后台自动进行的。JRE 会负责回收那些不再使用的内存,这种机制被称为垃圾回收(GarbageCollection, GC) 。通常JRE 会提供一个后台线程来进行检测和控制, 一般都是在CPU 空闲或内存不足时自动进行垃圾回收,而程序员无法精确控制垃圾回收的时间和顺序等。
Java 的堆内存是一个运行时数据区,用以保存类的实例(对象), Java 虚拟机的堆内存中存储着正在运行的应用程序所建立的所有对象,这些对象不需要程序通过代码来显式地释放。一般来说,堆内存的回收由垃圾回收器来负责,所有的NM 实现都有一个由垃圾回收器管理的堆内存。垃圾回收是一种动态存储管理技术,它自动释放不再被程序引用的对象,按照特定的垃圾回收算法来实现内存资源的自动回收功能。
在C/C++中,对象所占用的内存不会被自动释放,如果程序没有显式释放对象所占用的内存,对象所占用的内存就不能分配给其他对象,该内存在程序结束运行之前将一直被占用;而在Java 中,当没有引用变量指向原先分配给某个对象的内存时,该内存便成为垃圾。JVM 的一个超级线程会自动释放该内存区。垃圾回收意味着程序不再需要的对象是"垃圾信息",这些信息将被丢弃。
当一个对象不再被引用时,内存回收它占领的空间,以便空间被后来的新对象使用。事实上,除释放没用的对象外,垃圾回收也可以清除内存记录碎片。由于创建对象和垃圾回收器释放丢弃对象所占的内存空间,内存会出现碎片。碎片是分配给对象的内存块之间的空闲内存区,碎片整理将所占用的堆内存移到堆的一端, NM 将整理出的内存分配给新的对象。
垃圾回收能自动释放内存空间,减轻编程的负担。这使Java 虚拟机具有两个显著的优点。
- 垃圾回收机制可以很好地提高编程效率。在没有垃圾回收机制时,可能要花许多时间来解决一个难懂的存储器问题。在用Java 语言编程时,依靠垃圾回收机制可大大缩短时间。
- 垃圾回收机制保护程序的完整性,垃圾回收是Java 语言安全性策略的一个重要部分。
垃圾回收的一个潜在缺点是它的开销影响程序性能。Java 虚拟机必须跟踪程序中有用的对象,才可以确定哪些对象是无用的对象,并最终释放这些无用的对象。这个过程需要花费处理器的时间。其次是垃圾回收算法的不完备性,早先采用的某些垃圾回收算法就不能保证100%收集到所有的废弃内存。当然,随着垃圾回收算法的不断改进,以及软硬件运行效率的不断提升,这些问题都可以迎刃而解。
Java 语言规范没有明确地说明JVM使用哪种垃圾回收算法,但是任何一种垃圾回收算法一般要做两件基本的事情:发现无用的对象;回收被无用对象占用的内存空间,使该空间可被程序再次使用。通常,垃圾回收具有如下几个特点。
- 垃圾回收器的工作目标是回收无用对象的内存空间,这些内存空间都是JVM 堆内存里的内存空间,垃圾回收器只能回收内存资源,对其他物理资源,如数据库连接、磁盘110 等资源则无能为力。
- 为了更快地让垃圾回收器回收那些不再使用的对象,可以将该对象的引用变量设置为null ,通过这种方式暗示垃圾回收器可以回收该对象。
- 垃圾回收发生的不可预知性。由于不同JVM采用了不同的垃圾回收机制和不同的垃圾回收算法,因此它有可能是定时发生的,有可能是当CPU 空闲时发生的,也有可能和原始的垃圾回收一样,等到内存消耗出现极限时发生,这和垃圾回收实现机制的选择及具体的设置都有关系。虽然程序员可以通过调用Runtime 对象的gc()或System.g()等方法来建议系统进行垃圾回收,但这种调用仅仅是建议,依然不能精确控制垃圾回收机制的执行。
- 垃圾回收的精确性主要包括两个方面: 一是垃圾回收机制能够精确地标记活着的对象; 二是垃圾回收器能够精确地定位对象之间的引用关系。前者是完全回收所有废弃对象的前提,否则就可能造成内存泄漏;而后者则是实现归井和复制等算法的必要条件,通过这种引用关系,可以保证所有对象都能被可靠地回收,所有对象都能被重新分配,从而有效地减少内存碎片的产生。
- 现在的JVM 有多种不同的垃圾回收实现, 每种回收机制因其算法差异可能表现各异,有的当垃圾回收开始时就停止应用程序的运行,有的当垃圾回收运行时允许应用程序的线程运行,还有的在同一时间允许垃圾回收多线程运行。
当编写Java 程序时, 一个基本原则是: 对于不再需要的对象,不要引用它们。如果保持对这些对象的引用, 垃圾回收机制暂时不会回收该对象,则会导致系统可用内存越来越少:当系统可用内存越来越少时, 垃圾回收执行的频率就越来越高,从而导致系统的性能下降。
2011 年7 月发布的Java 7 提供了Gl 垃圾回收器来代替原有的并行标记/清除垃圾回收器(简称CMS ) 。
2014 年3 月发布的Java 8 删除了HotSpot NM 中的永生代内存( PermGen ,永生代内存主要用于存储一些需要常驻内存、通常不会被回收的信息) , 而是改为使用本地内存来存储类的元数据信息,并将之称为: 元空间( Metaspace ) ,这意味着以后不会再遇到java . lang. OutOfMemoryError : PermGen 错误(曾经令许多Java 程序员头痛的错误)。
2017 年9 月发布的Java 9 彻底删除了传统的CMS 垃圾回收器,因此运行JVM 的DefNew + CMS 、ParNew + SerialOld 、Incremental CMS 等组合全部失效。Java 命令(该命令负责启用jvm运行Java 程序)以前支持的以下GC 相关选项全部被删除。
- -Xincgc
- -XX:+CMSIncrementalMode
- -XX:+UseCMSCompactAtFullCollection
- -XX:+CMSFullGCsBeforeCompaction
- -XX :+UseCMSCollectionPassing
此外, -XX :+UseParNewGC 选项也被标记为过时, 将来也会被删除。
Java 9 默认采用低暂停( low-pause ) 的G1垃圾回收器, 并为G1 垃圾回收器自动确定了几个重要的参数设置, 从而保证G1 垃圾回收器的可用性、确定性和性能。如果部署项目时为Java 命令指定了-XX: +UseConcMarkSweepGC 选项希望启用CMS 垃圾回收器,系统会显示警告信息。

浙公网安备 33010602011771号