Java8新特性简介
# Java8新特性
提示
学习 Java 每个版本的新特性,紧跟时代,与本内容一起学习 Java8 的新特性吧。
Java 8新特性汇总
# 一、Java8新特性简介
Java 8 (又称为 JDK 1.8) 是 Java 语言开发的一个主要版本。Java 8 是 Oracle 公司于 2014 年 3 月发布,可以看成是自 Java 5 以来最具革命性的版本。Java 8 为 Java 语言、编译器、类库、开发工具与 JVM 带来了大量新特性。
- 速度更快
- 代码更少(增加了新的语法:Lambda表达式)
- 引入强大的
Stream APl
- 优化 Fork/Join 框架,更加容易写并行代码
- 最大化减少空指针异常:
Optional
Nashorn
引擎,允许在JVM上运行JS
应用- 时间 API等等...
其中最为核心的是 Lambda 表达式和 Stream API。
# 二、哈希结构
Java8 优化了哈希算法,JDK7 之前是数组 + 链表,当两个不同对象经过哈希算法算出的下标相同时,则该数组下标处形成链表,旧的在链表最后面。但是这样产生很长的链表时,恰巧我们需要的对象在最后一个,则需要从头遍历到尾,效率低。
JDK8 变成了数组 + 链表 + 红黑树,当链表的长度超出 8 时,并且整个数组的长度超出 64 时,则链表转为红黑树,即一种二叉树,这样减少了从头遍历到尾的效率低问题,只需要在二叉树的一方遍历,找出对象即可,不需要像全部遍历到最后。
# 三、JVM方法区
# 1. 什么是 JVM 方法区?
JVM 方法区(Method Area)是 Java 内存模型的一部分,主要用于存储类的结构信息、常量、静态变量、方法代码等数据。它是一个逻辑上的概念,并且具体的实现方式与 JVM 的实现有关。
在 Java 7 及之前版本中,方法区的实现称为永久代(PermGen)。而从 Java 8 开始,方法区被移到了**元空间(Metaspace)**中。
# 2. 方法区的演变
在 Java 的发展历程中,方法区经历了从永久代到元空间的变化,这种变化主要是为了优化内存管理,减少 OutOfMemoryError
异常的发生。
版本 | 方法区的变化 |
---|---|
JDK 1.6 及以前 | 使用永久代,静态变量和常量池存储在永久代中 |
JDK 1.7 | 逐步去掉永久代,字符串常量池和静态变量移出永久代,存储在堆中 |
JDK 1.8 | 完全移除永久代,类型信息、字段、方法、常量移到元空间(本地内存) |
元空间(Metaspace) 的引入使得 JVM 的内存管理更加灵活,元空间不再使用 JVM 堆内存,而是使用本地内存(Native Memory)。这意味着元空间的大小只受系统内存的限制,从而减少了 OutOfMemoryError: PermGen space
异常的发生。
# 3. 为什么永久代被替代为元空间?
永久代存在以下问题,促使了元空间的出现:
空间大小难以确定:永久代的空间需要提前设定,对于一些动态加载类较多的应用(如 Web 应用),永久代的空间很难准确预估,导致
PermGen
空间不足,抛出OutOfMemoryError
异常。难以调优:在永久代中调优 GC(垃圾回收)较为困难,尤其是在 Full GC 过程中,永久代的回收往往效果不佳,导致频繁的 Full GC,从而影响系统性能。
垃圾回收机制局限:虽然方法区的垃圾回收(如类卸载)是可选的,但实际操作中常常效果不理想。元空间的引入通过使用本地内存,缓解了这些问题,并且能够更灵活地管理类元数据。
# 4. 方法区的垃圾回收
方法区的垃圾回收主要涉及以下两个方面:
- 常量池中的废弃常量:如字符串常量池中的不再使用的常量。
- 不再使用的类:如类卸载后留下的类型信息。
值得注意的是,方法区的垃圾回收与堆内存相比,回收条件更加苛刻,且效果往往不如预期。不过,类卸载的回收对于长期运行的应用仍然是必要的,特别是在一些动态加载大量类的应用中。
# 5. 元空间的配置与优化
在 Java 8 及以后版本中,可以通过以下 JVM 参数来配置元空间:
-XX:MetaspaceSize
:设置元空间的初始大小。当元空间达到这个大小时,将触发垃圾回收以进行类卸载。-XX:MaxMetaspaceSize
:设置元空间的最大大小。如果不设置该参数,元空间的大小仅受系统内存限制。-XX:MinMetaspaceFreeRatio
和-XX:MaxMetaspaceFreeRatio
:控制垃圾回收后元空间的最小和最大空闲比例。
# 6. 代码示例:触发元空间 OOM 异常
以下示例展示了如何通过动态生成大量类,触发元空间的 OutOfMemoryError
异常:
import javassist.ClassPool;
public class MetaspaceOOMDemo {
public static void main(String[] args) throws Exception {
// 使用 Javassist 动态生成类
ClassPool pool = ClassPool.getDefault();
for (int i = 0; ; i++) {
Class<?> clazz = pool.makeClass("com.example.GeneratedClass" + i).toClass();
System.out.println("Loaded class: " + clazz.getName());
}
}
}
2
3
4
5
6
7
8
9
10
11
12
代码解析:
- 该示例使用了 Javassist 库来动态生成类,并不断加载这些类,最终导致元空间耗尽,抛出
OutOfMemoryError
异常。
# 7. 元空间的实际应用场景
元空间在一些动态加载和卸载大量类的应用中,如基于 OSGi 的模块化应用、动态代理框架和一些大型 Web 应用中发挥了重要作用。通过适当地配置元空间参数,可以避免由于类加载过多而导致的内存不足问题,提高应用的稳定性。
总结
Java 8 中,永久代被元空间取代,大大提高了 JVM 的内存管理能力。元空间使用本地内存,使得方法区的大小不再受限于 JVM 堆,从而解决了永久代空间不足的问题。在实际开发中,合理配置元空间参数对于优化 JVM 性能、避免内存不足问题至关重要。