JAVA复习题
1. JDK、JRE、JVM 三者的关系
JDK、JRE和 JVM是 Java 平台的核心组成部分,它们之间的关系如下:
JDK(Java Development Kit):JDK 是 Java 开发工具包,它包含了 JRE 和用于开发 Java 程序的工具,如编译器(javac)、调试器(jdb)和其他各种工具。简单来说,JDK 是为了开发 Java 程序而提供的完整软件包。
JRE(Java Runtime Environment):JRE 是 Java 运行时环境,它包含了 JVM 和运行 Java 应用程序所需的库。JRE 是为了让 Java 程序能够运行而提供的软件环境。
JVM(Java Virtual Machine):JVM 是 Java 虚拟机,是 JRE 的一部分,负责运行 Java 字节码。它是 Java 程序与操作系统之间的桥梁,确保了 Java 程序的跨平台特性。
关系总结:
- JDK 包含 JRE:因为开发 Java 程序不仅需要运行环境,还需要编译器和其他开发工具。
- JRE 包含 JVM:运行 Java 程序需要 JVM 来执行字节码。
2. JDK8 的新特性
JDK8 引入了许多新特性和改进,以下是一些主要的新特性:
- Lambda 表达式:允许使用更简洁的语法来编写匿名方法,提高了代码的简洁性和易读性。
- Stream API:提供了一种新的抽象级别和一系列流操作,用于处理数据集合(如集合、数组等),支持并行处理。
- 默认方法(Default Methods):允许在接口中添加非抽象方法,这些方法可以提供方法的默认实现。
- Optional 类:用于减少空指针异常的风险,提供一个包装器对象来表示可能为空的值。
- 日期和时间 API:引入了新的日期时间 API(java.time),解决了旧版 Date 和 Calendar API 的问题。
- 并行数组排序:改进了 Arrays.parallelSort 方法,可以更高效地对数组进行并行排序。
- JavaScript 引擎 Nashorn:JDK8 引入了一个新的 JavaScript 引擎 Nashorn,它完全用 Java 编写,允许 Java 程序与 JavaScript 代码交互。
3. JVM 内存结构,堆栈的区别
JVM 的内存结构主要包括以下几部分:
- 方法区:用于存储已被虚拟机加载的类信息、常量、静态变量等数据。
- 堆:是 JVM 管理的最大内存区域,用于存放对象实例。几乎所有的对象实例都在这里分配内存。
- 栈:线程私有,每个线程创建时都会有自己的栈,用于存储局部变量、基本数据类型值和对象的引用变量。
- 程序计数器:线程私有,每个线程都有一个程序计数器,是线程私有的内存空间,用来存储指向下一条指令的地址。
- 本地方法栈:用于虚拟机使用到的 Native 方法服务。
堆和栈的区别:
- 生命周期:栈的生命周期与线程相同,当线程结束时,栈会随之消失;而堆是所有线程共享的,生命周期跟随应用程序的生命周期。
- 存储内容:栈存储局部变量和对象的引用,而堆存储对象实例和数组。
- 管理方式:栈内存的分配和回收是自动的,由系统完成;而堆内存的分配和回收是通过垃圾回收器来管理的。
- 性能:栈的操作通常比堆更快,因为它在内存中是连续分配的,而堆的内存分配可能是分散的,需要更多的管理开销。
4. Java 会存在内存泄漏吗?
Java 中会存在内存泄漏。尽管 Java 有垃圾回收机制来自动管理内存,但内存泄漏仍然可能发生。内存泄漏指的是应用程序中存在的导致无法被垃圾回收器回收的对象,这些对象占据了内存但不再被程序中的任何部分所需要。以下是一些可能导致内存泄漏的情况:
- 长生命周期对象持有短生命周期对象的引用,导致短生命周期对象不能被回收。
- 静态集合类(如 static HashMap)如果不当使用,可能会随着时间的推移持续增长,因为静态属性在程序生命周期内一直存在。
- 内部类持有外部类的引用,且内部类的实例被长时间持有。
- 使用 ThreadLocal 时,如果没有正确清理,可能导致内存泄漏。
- 各种第三方库或框架可能因为设计不当导致内存泄漏。
5. 有什么办法可以把方法区内存撑爆
方法区是 JVM 堆内存的一部分,用于存储已被虚拟机加载的类信息、常量、静态变量等数据。以下是一些可能导致方法区内存撑爆的操作:
- 大量类加载:如果连续不断地加载大量类(例如,使用反射机制动态生成类),将会消耗方法区的内存。
- 静态字段和常量:在类中定义大量的静态字段或常量,特别是大型字符串常量,会占用方法区的空间。
- 大量内部类:定义很多内部类,因为内部类的类信息也需要存储在方法区。
- 大型字符串常量池:大量使用字符串常量,特别是大型字符串,会填充方法区的字符串常量池。
撑爆方法区通常会导致 OutOfMemoryError
,具体为 PermGenSpace
(JDK 8 之前)或 Metaspace
(JDK 8 及以后)错误。
6. 常见的垃圾回收机制
Java 垃圾回收(GC)机制负责自动管理内存,以下是几种常见的垃圾回收算法和机制:
-
标记-清除(Mark-Sweep):此算法分为标记和清除两个阶段,首先标记出所有活动的对象,然后清除未被标记的对象。这个过程中可能会产生内存碎片。
-
标记-整理(Mark-Compact):在标记阶段后,将所有存活的对象压缩到内存的一端,然后清理边界以外的内存。这种方法减少了内存碎片,但需要移动对象。
-
复制(Copying):将可用内存划分为两块,每次只使用其中一块。当一块内存使用完毕后,将存活的对象复制到另一块内存中,然后清理掉旧的内存块。
-
分代收集:将对象按照生命周期分为新生代和老年代,分别采用不同的收集策略。新生代经常发生垃圾回收,通常使用复制算法;老年代则使用标记-清除或标记-整理算法。
-
增量收集和并发收集:这些机制旨在减少垃圾回收时对应用程序的影响。增量收集将垃圾回收分成多个小部分,交错在应用程序的运行中执行;并发收集则是在应用程序线程并发运行时执行垃圾回收。