这些技巧可能会让你的应用程序性能提高几倍。
程序性能优化是一个复杂的主题。通常需要结合特定场景进行性能分析,并找出瓶颈提出优化建议。然而,如果我们忽略了通常的编码细节并改进多个细节的性能,那么优化这些细节的累积性能收益也是相当可观的。今天,让我们谈谈一些Java代码细节优化的技巧。
复杂的字符串连接操作使用StringBuilder
早在我职业生涯的初期,当进行字符串连接操作时,我肯定会使用String a=c+e+d
的写法。这种Java语法糖对于开发人员来说太方便了。但是,如果你在循环中使用“+”这样的操作符,就必须小心了。
String a=null;
for(int i=0;i<1000;i++) {
a=a+i;
}
我们都知道,字符串是不可变的,所以在循环中对字符串的每次赋值都会在堆内存中创建一个新的字符串对象。在循环体内,你将反复创建多个无用的对象,这不仅会占用内存空间,而且还会影响垃圾回收时间。所以请听我一句话,如果你遇到循环中的字符串拼接,请使用StringBuilder而不是“+”。
使用ThreadPoolExecutor而不是手动创建线程
许多初学者在编写代码时喜欢创建自己的线程,这是一种危险的做法。
如果此线程创建需要处理大量请求,则很可能导致程序频繁地创建和销毁线程并频繁地切换线程上下文,浪费CPU资源甚至耗尽内存。
因此,建议使用ThreadPoolExecutor并配置适当数量的核心线程和最大线程数。
集合预分配适当的容量
我们都知道,像ArrayList、HashMap和ConcurrentHashMap这样的集合类可以自动扩展,但是这种自动扩展涉及底层数组的复制和迁移。如果扩展频繁,肯定会影响程序的性能。因此,如果你可以估计集合的大致容量,请直接进行初始化分配。
使用枚举而不是常量类
很多人特别喜欢在项目中创建一个常量类,就像这样。
各位,你可以用枚举来代替它吗?枚举是强制验证而不出错的。同时,使用枚举类的性能更高。而且,使用枚举的优势更大。它可以与策略模式一起使用,以提高程序的可扩展性。例如:
看,你可以根据需要动态选择下载文件的策略。你可以使用FileType.EXCEL.download()
来下载一个Excel格式的文件。
使用NIO而不是传统的IO
传统的IO已经过时了。强烈建议使用NIO来代替传统的IO。因为传统的IO使用阻塞IO模型,在请求数据后,线程会被阻塞从数据准备到数据可读性。
此外,如果传统的IO想要将数据写入网络卡,需要先将数据写入堆内存,然后将数据复制到堆外的一块内存中,然后将数据从用户状态复制到内核状态缓冲区。
最后,CPU通知DMA将数据写入网络卡,经历了总共三次复制。NIO不仅使用多路复用IO模型,而且可以使用直接内存来减少数据复制的次数,从而提高性能。
使用位移操作
如果你阅读过JDK的一些源代码,例如HashMap,你会看到代码中有许多位移操作。因为JDK是相对较低级别的代码,对性能的追求也是极致的。在我们日常编码中,可以使用位移操作替换一些乘除运算,例如将a >> 1
替换为a / 2
,将a * 16
替换为a << 4
。## 尝试使用单例模式
如果我们设计的类不需要考虑线程安全,请使用单例模式来使用这个类,这样可以节省内存。幸运的是,我们使用的spring框架默认情况下Java beans是单例的。
减少锁的粒度
假设我们有一个共享文档编辑功能,用户将同时编辑共享文档。为了确保文档的正确性,我们需要使用同步来确保线程安全。许多初学者可能会这样写。
如果使用上述方法,则只有一个线程可以进入同步代码块以执行,其他线程只能挂起等待,即使这些线程可能会编写不同的文档。我们可以通过减少锁的粒度来提高性能。
不要随意使用静态变量
如果一个对象被定义为静态变量,这个变量的引用不容易被垃圾收集器回收。
public class Test{
public static A a = new A();
}
静态变量“a”的生命周期与测试类相同。只要测试类型没有卸载,“a”的引用对象就会驻留在内存中,直到程序终止。
使用基本数据类型
你还可以在应用程序中使用基本数据类型来减少内存消耗并提高程序性能。如果可以使用int,请不要使用其包装类型integer,或者使用double代替Double。
基本数据类型的包装类实例存储在堆内存中,每次使用都会在堆内存中创建一个实例。如果使用基本数据类型,则数据存储在堆栈帧中,堆栈的访问速度比堆的访问速度快得多。
感谢阅读。希望本文能给你一些关于性能优化的思路。
评论(0)