首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >Java 自动装箱对性能的影响大还是小?如何解决 Java 自动装箱性能问题?

Java 自动装箱对性能的影响大还是小?如何解决 Java 自动装箱性能问题?

作者头像
用户2242639
发布于 2021-06-29 07:36:00
发布于 2021-06-29 07:36:00
1.6K00
代码可运行
举报
文章被收录于专栏:Java经验总结Java经验总结
运行总次数:0
代码可运行

每个Java开发人员都知道,Java中有八种原始类型:

  • byte b = 5;
  • short s = 10;
  • int i = 30;
  • long l = 22;
  • float f = 41;
  • double d = 99;
  • char c = ‘a’;
  • boolean b = true;

原始类型代表了用代码表示数据的最简单,最直接的方法。即使 Java 中最复杂的类也可以简化为它们所表示的原始数据类型集。但是原始类型不是对象,这带来了一个问题。

例如, JDK 中的所有收集类都将数据作为对象保存。如果开发人员有一组要存储在 ArrayList 中的 int 值,则无法完成。当然,除非他们使用相应的包装器类或利用 Java 中的自动装箱功能。

Java自动装箱示例

对于每种基本类型,都有一个对应的包装器类:

  • Integer for int
  • Character for char
  • Double for double
  • ...

从历史上看,如果开发人员想在集合类中存储一组 double ,则必须将其从原始类型转换为包装类:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
int x = 10;
ArrayList<E> list = new ArrayList();
// list.add(10); Pre JDK 1.5 autoboxing would not work
Integer wrapper = Integer.valueOf(x);
list.add(wrapper);

但是, JDK 1.5 版引入了一项称为Java原语类型自动装箱的功能。这意味着在较新的 JDK 上,当在需要引用类型的任何地方使用基本类型时,将自动创建包装器类。因此,在 JDK 1.5 之后的 JVM 版本上,上述用例无需使用包装器类。Java 装箱和原始类型的自动装箱将为您处理:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
int x = 10;
ArrayList<E> list = new ArrayList();
list.add(10); // This is primitive type autoboxing in Java 
//Integer wrapper = Integer.valueOf(x);
//list.add(wrapper);

自动装箱Java如何损害性能

我一直以为,当Java引入原始类型装箱和装箱时,也实现了JVM级别的优化,以解决与Java自动装箱相关的任何性能问题。我认为在时钟周期,垃圾回收和内存消耗方面,在包装器类和原始类型之间移动是相对平稳的操作。

我不可能错得更多。

这是高度人为设计的用例,其灵感主要来自Marcus Hirt的 JMC示例。

首先,我们创建一个简单的组件,用作int值的包装器类。该类名为 SnoopInt :

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
package com.mcnz.jfr.jmc;
public final class SnoopInt {
  final int id;
  SnoopInt(int id) { this.id = id; }
  int getId() { return id; }
}

Java自动装箱类

然后,我们有了一个可运行的类,该类将一百万个原始类型的 int 值推入映射。

然后,我们复制地图中的所有值,然后遍历原始地图以确认副本中的所有值也都在原始文件中。这是一个人为的示例,但是它给JVM带来了负担,并且在垃圾回收和内存性能指标方面产生了一些有趣的结果。

有很多自动装箱的 Java 基本类型,因此我将类命名为 MikeTyson :

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
package com.mcnz.jfr.jmc;
import java.util.*;

public final class MikeTyson implements Runnable {
  private final Map<Integer, SnoopInt> map = new HashMap<>();
  public MikeTyson() {
    for (int i = 0; i < 1_000_000; i++) {
      map.put(i, new SnoopInt(i));
    }
  }
 
  public void run() {
    long yieldCounter = 0;
    while (true) {
      Collection copyOfValues = map.values();
      for (SnoopInt snoopIntCopy : copyOfValues) {
        if (!map.containsKey(snoopIntCopy.getId()))
          System.out.println("Now this is strange!");
        if (++yieldCounter % 1000 == 0)
          System.out.println("Boxing and unboxing");
          Thread.yield();
      }
    }
  }
  
  public static void main(String[] args) throws java.io.IOException {
    ThreadGroup threadGroup = new ThreadGroup("Workers");
    Thread[] threads = new Thread[8];
    for (int i = 0; i < threads.length; i++) {
      threads[i] = new Thread(threadGroup, new MikeTyson(), "Allocator Thread " + i);
      threads[i].setDaemon(true);
      threads[i].start();
    }
    System.out.print("Press  to quit!");
    System.out.flush();
    System.in.read();
  }
}

自动装箱和Java Mission Control

使用 Java Flight Recorder 和 JDK Mission Control Eclipse 插件对该程序进行快速分析会触发红色警告,将“原始到对象转换”标记为有问题。自动装箱导致性能问题。

Java 原语类型的装箱和拆箱会导致 JVM 性能问题。

此外,当您检查 Java Mission Control 的垃圾收集指标时,您会发现垃圾收集不在图表中:

当使用自动装箱功能时,Java Mission Control 显示了猖 ramp 的垃圾回收例程会影响性能。

修复Java中的自动装箱

您如何解决 Java 自动装箱性能问题?

开发人员只需更改几行代码即可解决该问题。如果在整个应用程序中使用 Integer 引用类型,则所有垃圾回收问题都将消失。

MikeTyson 类的构造函数有一个小的变化:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
public MikeTyson() {
  for (int i = 0; i < 700_000; i++) {
    map.put(Integer.valueOf(i), 
              new SnoopInt(Integer.valueOf(i)));
  }
}

并且自定义包装器类也将更新为也使用 Integer 引用类型:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
public final class SnoopInt {
  final Integer id;
  SnoopInt(Integer id) { this.id = id; }
  Integer getId() { return id; }
}

使用JFR和JMC监视和配置文件

进行了这些较小的更改之后,再次启动 Java Flight Recorder,Java 基本类型装箱和拆箱性能问题就消失了。垃圾回收没有明显增加,并且在 Java Flight Recorder 运行之后, Java Mission Control 不会报告任何从原语到对象的转换问题。

我一直认为自动装箱 Java 对性能的影响很小,但是我还是错了。性能影响可能很大。

这就是为什么使用 Java 的 JVM Flight Recorder 和 JDK Mission Control 工具之类的工具不断地分析应用程序的重要性。假设还不错,只要存在一种验证它们的机制即可,正如此 Java 自动装箱性能示例清楚地表明的那样。

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2021-02-23,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 Java经验总结 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • Java自动装箱示例
  • 自动装箱Java如何损害性能
  • Java自动装箱类
  • 自动装箱和Java Mission Control
  • 修复Java中的自动装箱
  • 使用JFR和JMC监视和配置文件
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档