提升 Java 应用程序的十个优化技巧

开发 前端
程序性能优化是一个复杂的话题。往往需要结合具体场景进行性能分析,找出瓶颈提出优化建议。但是,假设我们平时很少关注细节的性能,那么这种情况下,优化这些细节所带来的收益也是相当可观的。接下来,我们就来说说Java代码细节优化的一些小技巧。

程序性能优化是一个复杂的话题。往往需要结合具体场景进行性能分析,找出瓶颈提出优化建议。但是,假设我们平时很少关注细节的性能,那么这种情况下,优化这些细节所带来的收益也是相当可观的。接下来,我们就来说说Java代码细节优化的一些小技巧。

复杂的字符串连接操作使用 StringBuilder

职业生涯早期,在做字符串连接操作的时候,肯定会这么写:String a=c+e+d,这个Java语法糖对于开发者来说太方便了。但是如果你在循环中使用“+”,那就得小心了。

String a=null;
for(int i=0;i<1000;i++) {
a=a+i;
}

我们都知道String 是不可变的,因此循环中对 string 的每一次赋值都会在堆内存中创建一个新的 String 对象。在一个循环体中,反复创建多个无用的对象,不仅会占用内存空间,还会影响GC时间。所以说,如果在循环中遇到字符串拼接,就使用 StringBuilder 而不是“+”

使用 ThreadPoolExecutor 避免手动创建线程

许多初学者喜欢在编写代码时创建线程,这是一种危险的做法。

如果这个线程的创建需要处理大量的请求,很可能导致你的程序频繁的创建和销毁线程,频繁的切换线程上下文,浪费CPU资源,甚至会耗尽内存。

因此,建议使用ThreadPoolExecutor,并配置合适的核心线程数和最大线程数。

为集合预分配适当的容量

我们都知道 ArrayListHashMap 和 ConcurrentHashMap 等集合类是可以自动扩容的,但是这种自动扩容涉及到底层数组的复制和迁移。如果扩容频繁,肯定会影响程序的性能。所以如果你能估计出大概的容量,请直接配置初始值。

使用枚举而不是常量类

很多人特别喜欢在项目中创建一个常量类,如下:

public class Constant {

public static final String TOKEN_HEADER = "x-request-token";

public static final Integer CODE_SUCCESS = 0;

public static final Integer CODE_REQUEST_FAILED = 1;

public static final Integer CODE_REQUEST_RUNNING = 2;
}

为什么不用枚举呢?Enum 有强制的类型验证。同时,使用枚举类的性能更高。并且使用 enum 还有更大的优势,它可以与策略模式一起使用来提高程序的可扩展性。例如:

public enum FileType {

EXCEL(".xlsx"){
@Override
public void download(String path) {
//do download excel file logic
}
}, CSV(".csv") {
@Override
public void download(String path) {
//do download csv file logic
}
};

private String suffix;

FileType(String suffix) {
this.suffix = suffix;
}

public String getSuffix() {
return suffix;
}

public abstract void download(String path);
}

如代码所示,你可以根据需要动态选择一种策略来下载文件,直接调用FileType.EXCEL.download(),无需关心代码细节。

使用 NIO 代替传统 IO

传统的 IO 已经过时了。强烈推荐使用 NIO 代替传统的 IO。因为传统IO采用阻塞IO模型,请求数据后,线程从数据准备到数据可读都是阻塞的。

而且,传统IO如果要往网卡写数据,需要先把数据写到堆内存,然后再把数据拷贝到堆外的一块内存,再从用户态拷贝数据到内核状态缓冲区。最后CPU通知DMA将数据写入网卡,一共经历了3次拷贝。NiO不仅采用了multiplex IO模型,还可以使用direct memory来减少数据拷贝次数,从而提高性能。

使用移位操作

如果你看过一些JDK的源代码,比如HashMap,你会发现代码中有很多移位操作。因为JDK是比较底层的代码,对性能的追求也是极致的。在我们日常的编码中,可以用移位运算来代替一些乘除运算,比如a >> 1 代替 a / 2a * 16 代替 a << 4

这个技巧也能在一定程度上提高性能,但是如果你不擅长,那就不要强求,因为当代计算机的性能已经非常强大了,没必要为了一个程序而牺牲代码的可读性。

尝试使用单例模式

如果我们设计一个不需要考虑线程安全的类,请用单例模式来使用这个类,这样可以节省内存。幸运的是,对于我们使用的spring框架,Java bean默认是单例的。

降低锁粒度

假设我们有一个共享文档编辑功能,用户会同时编辑共享文档。为了保证文件的正确性,我们需要使用线程安全synchronized来保证。很多初学者可能会这样写。

public class Test{
private Object lock = new Object();

public void write(String username, String fileName) {
synchronized(lock) {
//do something
}
}
}

如果采用上述方式,只有一个线程可以进入同步代码块执行,其他线程只能挂起等待,即使这些线程可能写入不同的文件。我们可以通过降低锁粒度来提高性能。

public class Test{

public void write(String username, String fileName) {
synchronized(fileName.intern()) {
//do something
}
}
}

不要随意使用静态变量

如果你熟悉JVM基础知识,那么就会知道如果一个对象被定义为静态变量,这个变量的引用就不容易被垃圾回收器回收。

public class Test{
public static A a = new A();
}

静态变量“a”的生命周期与测试类相同。只要测试类型没有被卸载,“a”的引用对象就会驻留在内存中,直到程序终止。

使用基本数据类型

在应用程序中使用基本数据类型来减少内存消耗并提高程序性能。如果可以使用 int,请不要使用其 Integer 包装类型,使用double 而不是 Double。

基本数据类型的包装类实例存放在堆内存中,每次使用都会在堆内存中创建一个。如果使用基本数据类型,数据存放在栈帧中,栈的访问速度可比堆快很多。

责任编辑:华轩 来源: 今日头条
相关推荐

2024-11-18 19:00:29

2019-08-16 02:00:46

AndroidGoogle 移动系统

2024-09-26 15:00:06

2011-09-05 12:58:28

Ubuntu应用程序

2010-06-18 09:17:51

jQuery

2023-09-05 06:48:46

云计算微服务领导者

2021-07-27 09:00:00

开发Web软件

2011-08-01 09:20:16

2023-05-24 16:48:47

Jupyter工具技巧

2019-09-26 08:33:51

Nginx技术Java

2023-10-23 14:14:10

SQL数据库

2021-04-16 08:11:07

程序体积优化

2024-08-27 12:21:52

桌面应用开发Python

2024-09-04 14:28:20

Python代码

2016-10-18 10:22:21

测试移动

2023-11-27 18:01:17

MySQL技巧

2011-05-19 13:15:44

PHP

2024-12-03 14:33:42

Python递归编程

2021-09-18 10:07:23

开发技能代码

2019-05-27 13:50:35

多云架构企业多云集成云计算
点赞
收藏

51CTO技术栈公众号