Java:性能优化细节11-20

这篇具有很好参考价值的文章主要介绍了Java:性能优化细节11-20。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

Java:性能优化细节11-20

11. 尽量合理的创建HashMap

当你要创建一个比较大的hashMap时,充分利用这个构造函数

public HashMap(int initialCapacity, float loadFactor);

合理初始化HashMap的容量和负载因子是提高HashMap性能的关键因素之一。HashMap在Java中是一种广泛使用的数据结构,用于存储键值对。正确地设置其初始容量(initialCapacity)和负载因子(loadFactor)可以显著减少哈希表重构的次数,从而优化性能。

初始容量(initialCapacity

  • 初始容量是哈希表在创建时的容量。
  • 如果预先知道将要存储的键值对数量,指定一个接近或稍微大于预计数量的初始容量可以减少或避免后续的扩容操作。
  • 扩容操作(即重新哈希)涉及到创建一个新的哈希表并重新计算每个元素的位置,这是一个相对昂贵的操作,特别是对于大型HashMap

负载因子(loadFactor

  • 负载因子是哈希表在其容量自动增加之前可以达到多满的一种度量。它是一个介于0和1之间的浮点数。
  • 默认的负载因子(0.75)提供了时间和空间成本之间的良好折衷。增加负载因子可以减少空间开销,但增加了查找成本(反映在大多数HashMap操作中,包括getput)。
  • 在设置负载因子时,需要权衡空间利用率和查询性能。如果预期HashMap中的元素会非常密集地填充,可以考虑使用稍高的负载因子;反之,如果性能是首要考虑,而空间不是问题,则可以使用较低的负载因子。

实践建议

  • 在创建HashMap时,如果有关于将要存储的元素数量的估计,应使用带有初始容量和负载因子的构造函数,以避免或减少扩容操作。
  • 对于极大或极小的数据集,可以适当调整负载因子以优化性能或空间使用率。
  • 记住,过大的初始容量可能会浪费内存,而过小的初始容量可能会导致频繁的扩容。找到合适的平衡点是关键。

通过这种方式,可以确保HashMap在性能和空间使用上都得到优化,特别是在处理大量数据时。这同样适用于HashtableVector等其他Java集合框架中的类,它们也提供了类似的构造函数来调整初始容量和负载因子。

12. 尽量减少对变量的重复计算

减少在循环中对变量或方法的重复计算是优化代码性能的一个有效手段。这种优化可以减少计算开销,特别是当循环体中的表达式涉及到复杂计算或调用成本较高的方法时。

示例优化分析

原始代码:

for(int i=0; i < list.size(); i++)

每次循环迭代都会调用list.size(),如果list.size()的调用成本较高,或者list是一个同步集合,这可能会导致性能瓶颈。

优化后的代码:

for(int i=0, len=list.size(); i < len; i++)

在循环开始之前计算了list.size()并将其存储在len变量中,这样每次循环迭代时就避免了对list.size()的重复调用。这种方式特别适用于循环次数较多的情况,可以显著减少总体的计算开销。

为什么这种优化有效

  • 减少调用开销:避免在每次迭代中重复调用方法,尤其是那些在循环执行期间不会改变返回值的方法。
  • 减少计算量:对于复杂的表达式,将其结果预先计算并存储可以避免每次迭代时的重复计算。

更多优化策略

  • 预先计算:如果循环体内有可以预先计算的表达式,应该将它们移出循环。
  • 简化表达式:简化循环条件和循环体内的表达式。例如,如果循环体内的某些计算不依赖于循环变量,可以将它们提前计算。
  • 使用增强的for循环:当遍历集合或数组时,考虑使用Java的增强for循环(for-each循环),它可以减少代码量并避免一些性能问题,尽管在某些情况下它可能不如传统for循环灵活。

13. 尽量避免不必要的创建

原始代码:

A a = new A();
    if(i==1){
    list.add(a);
}

优化建议

优化后的代码示例将对象a的创建移入了条件判断内部,这样对象a只有在条件满足时才会被创建,从而避免了不必要的对象创建:

if(i == 1) {
    A a = new A();
    list.add(a);
}

注意点

  • 作用域限制:将对象a的创建移到条件语句内部会限制a的作用域仅在条件语句内,如果后续代码需要使用a,这种方式可能会引入作用域相关的问题。

  • 条件外引用:如果需要在条件外部引用a,则需要确保a在条件外部有定义,并且在使用前已经正确初始化。例如:

    A a = null;
    if(i == 1) {
        a = new A();
    }
    if(a != null) {
        list.add(a);
    }
    

更广泛的应用

  • 懒加载:这种优化思路不仅适用于简单的条件判断,还可以应用于更复杂的场景,如对象的懒加载(按需创建)。
  • 资源管理:在处理资源密集型对象时(如文件流、数据库连接等),确保仅在需要时才创建和打开资源,使用完毕后及时关闭,可以显著降低资源消耗和提高应用性能。

14. 尽量在finally块中释放资源

程序中使用到的资源应当被释放,以避免资源泄漏,这最好在finally块中去做。不管程序执行的结果如何,finally块总是会执行的,以确保资源的正确关闭。

在Java中,使用资源如文件流、数据库连接或网络连接时,必须确保这些资源在使用完成后被正确关闭,以避免资源泄漏,这可能导致性能下降和资源耗尽。

使用finally块释放资源

try-catch语句中,finally块提供了一个确保执行清理工作的机制,不管是否发生异常,finally块中的代码都会执行。因此,将资源释放放在finally块中是一个好习惯,例如:

FileInputStream in = null;
try {
    in = new FileInputStream("file.txt");
    // 使用资源
} catch (IOException e) {
    e.printStackTrace();
} finally {
    if (in != null) {
        try {
            in.close(); // 确保资源被关闭
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

使用try-with-resources语句

Java 7引入了try-with-resources语句,进一步简化了资源管理。此语法确保实现了AutoCloseable接口的资源会在语句结束时自动关闭,即使出现异常也是如此。使用try-with-resources可以使代码更简洁,减少出错的可能:

try (FileInputStream in = new FileInputStream("file.txt")) {
    // 使用资源
} catch (IOException e) {
    e.printStackTrace();
}

在这个示例中,FileInputStream资源在try块结束时会自动关闭,无需显式地在finally块中关闭它。这种方式不仅减少了代码量,还提高了代码的可读性和可维护性。

最佳实践

  • 对于Java 7及以上版本,优先使用try-with-resources语句自动管理资源。
  • 如果因为某些原因不能使用try-with-resources(如使用的Java版本较低),确保在finally块中释放资源。
  • 在释放资源时,考虑到可能的NullPointerException和其他异常,确保资源释放代码的健壮性。

15. 尽量使用移位来代替’a/b’的操作

在编程中,特别是在性能敏感的应用中,使用位移操作代替除法是一种优化性能的常见手段。位移操作在底层硬件上通常比除法操作要快得多,因为它们可以直接通过移动数字的位来实现,而不涉及复杂的算术运算。

示例说明

使用位移操作来优化除以2的幂次的操作:

  • 除以4等价于向右位移2位(a >> 2)。
  • 除以8等价于向右位移3位(a >> 3)。

注意事项

  • 适用范围:这种优化只适用于除数是2的幂次(如2、4、8、16等)的情况。对于非2的幂次的除法操作,不能直接用位移操作来替代。
  • 有符号和无符号移位:Java提供了两种右位移操作。>>是有符号右移,它会保留符号位(即负数在移位后仍然是负数),而>>>是无符号右移,不考虑符号位,空位都以0填充。根据具体情况选择适当的操作。
  • 精度问题:位移操作与除法操作在某些情况下的结果可能不完全相同,特别是涉及到负数时。位移操作是直接对二进制位进行操作,不会四舍五入或进行任何形式的调整。

注释的重要性

由于位移操作对于不熟悉二进制运算的人可能不太直观,因此,在使用位移操作时添加清晰的注释是一个好习惯。注释应解释位移操作的目的,以及它是如何等价于特定的除法操作的。例如:

// 使用位移操作来实现除以8,等价于 a / 8
int num = a >> 3;

这样做可以帮助维护代码的其他人员更容易理解代码的意图,保证代码的可读性和可维护性。

16.尽量使用移位来代替’a*b’的操作

使用位移操作来代替乘法操作,尤其是乘以2的幂次数时,确实可以提高计算效率。位移操作直接在二进制级别上移动位,比传统的乘法操作要快,因为它避免了复杂的算术运算过程。这种优化手段在性能敏感的应用中特别有价值,如图形处理、游戏开发和低级系统编程等领域。

示例说明

使用位移操作来优化乘以2的幂次的操作:

  • 乘以4等价于向左位移2位(a << 2)。
  • 乘以8等价于向左位移3位(a << 3)。

注意事项

  • 适用范围:这种优化只适用于乘数是2的幂次(2、4、8、16等)。对于其他乘数,不能使用位移操作来替代。
  • 结果的范围:需要注意的是,位移操作可能导致整数溢出,尤其是当操作的数值很大,接近其类型(如intlong)的最大值时。因此,使用位移操作时要考虑结果是否会超出预期的数值范围。
  • 有符号数:对于有符号数,左位移操作会影响符号位。在进行位移操作时,应确保不会不小心改变了数值的符号。

17. 尽量确定StringBuffer的容量

StringBuffer 的构造器会创建一个默认大小(通常是16)的字符数组。在使用中,如果超出这个大小,就会重新分配内存,创建一个更大的数组,并将原先的数组复制过来,再丢弃旧的数组。在大多数情况下,你可以在创建 StringBuffer的时候指定大小,这样就避免了在容量不够的时候自动增长,以提高性能。

如:

StringBuffer buffer = new StringBuffer(1000); 

正确初始化StringBuffer(或其后继者StringBuilder)的容量可以显著提高字符串处理的性能,尤其是在处理大量字符串拼接操作时。这是因为每次StringBuffer(或StringBuilder)容量不足以容纳更多的字符时,它都需要执行一个扩容操作,这包括分配一个新的更大的数组,复制旧数组中的内容到新数组,然后丢弃旧数组。这个过程不仅消耗时间,还会增加临时内存的使用,从而可能影响垃圾收集器的效率。

为什么指定容量是个好主意

  • 减少扩容次数:预先估计所需容量并在StringBuffer初始化时指定,可以最小化或完全避免扩容操作,从而提高性能。
  • 减少内存开销:通过避免不必要的数组复制和临时对象创建,可以减少内存使用,特别是在大量字符串操作的上下文中。

StringBuffer vs StringBuilder

虽然这里讨论的是StringBuffer,但对于单线程环境,StringBuilder通常是更好的选择,因为它不是线程安全的,因此不会因为同步操作而产生额外的性能开销。和StringBuffer一样,你也可以在创建StringBuilder实例时指定初始容量。

使用建议

  • 预估容量:在使用StringBufferStringBuilder之前,尽量预估最终字符串的长度,并据此指定一个初始容量。这可能需要根据使用场景进行一些经验估计。
  • 单线程使用StringBuilder:如果你的代码是在单线程环境下运行,考虑使用StringBuilder代替StringBuffer,因为前者在没有线程安全需求时提供了更好的性能。
  • 动态估计:在某些情况下,如果你事先不知道需要多少容量,可以考虑根据动态数据来估计容量。例如,如果你在循环中添加字符串,可以根据循环次数和平均字符串大小来计算一个大致的容量需求。

18. 尽量早释放无用对象的引用

合理管理对象引用对于优化Java程序的内存使用和性能非常重要。虽然Java虚拟机(JVM)的垃圾收集器(GC)会自动回收无用对象占用的内存,但是合理地控制对象的生命周期可以更早地释放内存,特别是在处理大对象或内存敏感的应用中。

局部变量的引用释放

对于方法中的局部变量,一旦变量超出了其作用域,就会自然地失去引用,其指向的对象也就变成了垃圾回收的候选对象。因此,通常情况下,无需显式地将局部引用变量设置为null。下面的做法是多余的:

public void test() {
    Object obj = new Object();
    // ...其他操作...
    obj = null; // 没有必要
}

长生命周期的对象引用释放

然而,在某些情况下,尤其是在对象的引用在长时间执行的方法中仍然保持活跃,但实际上已经不再需要时,显式地释放这些引用(即将引用设置为null)可能有助于垃圾收集器更早地回收这些对象,从而减少内存占用,尤其是在如下情况:

public void test() {
    Object obj = new Object();
    // ...其他操作...
    obj = null; // 在执行耗时或耗内存的操作前显式释放引用
    
    // 执行耗时,耗内存操作;或调用耗时,耗内存的方法
}

最佳实践

  • 对于短生命周期的局部变量,无需显式设为null:方法执行完毕后,局部变量会自动失去作用域,因此它们引用的对象会自然成为垃圾回收的候选。
  • 在长时间运行的方法中及时释放不再需要的对象引用:如果方法执行时间很长,且某些对象在方法中早期就已经不再需要,那么将这些对象引用设为null可以帮助垃圾收集器更早回收这些对象。
  • 谨慎使用null:虽然在某些情况下设为null有助于内存管理,但过度使用可能会使代码难以理解和维护,甚至引入空指针异常的风险。因此,仅在必要时才这样做,并确保这种做法的目的和理由是清晰的。

19. 尽量避免使用二维数组

在Java中,数组是存储在堆内存上的对象。一个二维数组,从本质上讲,是一个数组的数组,即外层数组的每个元素都是另一个数组的引用。这种结构确实比一维数组使用更多的内存,主要原因包括:

  1. 引用开销:在二维数组中,外层数组包含了对内层数组的引用。每个引用本身就是一个额外的内存开销。
  2. 对象头开销:Java中的每个对象都有一个对象头(包含类指针、垃圾收集信息等),因此每个内层数组都会有这样的开销。
  3. 内存对齐:Java虚拟机可能会对对象数据进行内存对齐,增加额外的填充以优化性能,这也可能导致额外的内存使用。

使用场景和替代方案

  • 使用场景:在需要表达多维数据结构时,二维数组是一种直观的选择。例如,在处理矩阵、网格或任何需要行和列结构的数据时。
  • 性能考虑:如果性能和内存效率是首要考虑的话,可以考虑其他数据结构作为替代。例如,可以使用一维数组来模拟二维数组,通过计算索引来访问元素,这样可以减少内存的开销,尤其是在处理非常大的数组时。
  • 替代方案:对于复杂的数据结构,还可以考虑使用集合类(如ArrayList),或者创建自定义类来更有效地管理数据和内存。

20. 尽量避免使用split

避免使用split方法的建议主要基于性能考虑。String.split()方法在Java中确实是基于正则表达式来分割字符串的,这意味着即使是简单的分割操作也涉及到正则表达式的编译和匹配过程,这在处理大量数据时可能会成为性能瓶颈。

性能影响

  • 正则表达式编译split方法内部使用正则表达式,每次调用时都需要编译正则表达式,这是一个相对昂贵的操作。
  • 复杂度:正则表达式的复杂度可能会对性能产生显著影响,特别是当表达式复杂或输入字符串较长时。

替代方案

  • 使用StringUtils.split:Apache Commons Lang库提供的StringUtils.split方法是一个不基于正则表达式的替代方案,它对于简单的分隔符分割操作来说,性能通常优于String.split()

  • 手动实现分割逻辑:对于非常特定的分割需求,手动实现分割逻辑(如使用循环和String.substring())可能会提供更好的性能。这种方法允许细粒度控制和优化,但可能会增加实现的复杂性。

  • 结果缓存:如果对相同的字符串执行相同的分割操作多次,考虑将结果缓存起来重用,以避免重复的分割操作。文章来源地址https://www.toymoban.com/news/detail-833303.html

使用建议

  • 需求分析:在选择使用split还是其他方法之前,应该根据实际需求进行分析。如果分割操作简单且不频繁,使用split可能是可接受的。对于高性能需求,考虑使用更高效的方法。
  • 性能测试:在决定最终实现时,进行性能测试比理论分析更为重要。不同的方法在不同的上下文和数据集上可能表现不同,实际测试可以帮助确定最合适的选择。

到了这里,关于Java:性能优化细节11-20的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • Android 开发必备知识点及面试题汇总(Android+Java+算法+性能优化+四大组件……),腾讯安卓开发面试

    5.请介绍下 AsyncTask的内部实现,适用的场景是 AsyncTask 内部也是 Handler 机制来完成的,只不过 Android 提供了执行框架来提供线程池来 执行相应地任务,因为线程池的大小问题,所以 AsyncTask 只应该用来执行耗时时间较短的任务, 比如 HTTP 请求,大规模的下载和数据库的更改不

    2024年04月15日
    浏览(44)
  • Unity性能优化篇(十四) 其他优化细节以及UPR优化分析器

    代码优化: 1. 使用AssetBundle作为资源加载方案。 而且经常一起使用的资源可以打在同一个AssetBundle包中。尽量避免同一个资源被打包进多个AB包中。压缩方式尽量使用LZ4,少用或不要用LZMA的压缩方式。如果确定后续开发不会升级Unity版本,则可以尝试启用打包选项BuildAssetBun

    2024年04月28日
    浏览(29)
  • Java 20在Windows 11下的简易安装教程

    撰写时间:2023年4月11日 目的:安装编程语言Java的编译程序,并验证程序在电脑上能否正常运行。 前言:Java 20 (Oracle JDK 20)——Oracle公司于2023年3月21日发布的Java的最新版本——提供了大量的的性能、稳定性和安全性改进,包括平台增强功能,可帮助开发人员提高工作效率并

    2024年02月04日
    浏览(63)
  • Java程序性能优化技巧

    在Java软件开发中,经常使用 try-catch 进行错误捕获,但是,try-catch 语句对系统性能而言是非常糟糕的。虽然在一次 try-catch中,无法察觉到它对性能带来的损失,但是,一旦try-catch被应用于循环之中,就会给系统性能带来极大的伤害。 以下是一段将try-catch应用于for循环内的示

    2024年02月11日
    浏览(33)
  • Java 体系性能优化工具

    Survive by day and develop by night. talk for import biz , show your perfect code,full busy,skip hardness,make a better result,wait for change,challenge Survive. happy for hardess to solve denpendies. 性能优化的是一个非常常见的需求。 需求: 1.oom 异常来说: 推荐MAT,elps you find memory leaks。 2.visualvm Display Local And Remot

    2024年02月11日
    浏览(39)
  • Java性能优化技巧

    1. 如果在静态构造器中有繁重的计算,也就是耗费CPU的逻辑代码,请检查其运行时间是否过度?如果是,将这些逻辑迁移到另外一个单独的帮助类中。 2. 在进行byte[]作为String的构造参数时,需要将byte数组的一部分做个复制拷贝,否则,构造器会为整个原始缓冲做一个临时拷

    2024年02月11日
    浏览(71)
  • Java架构师缓存性能优化

    想学习架构师构建流程请跳转:Java架构师系统架构设计

    2024年02月07日
    浏览(31)
  • java使用双异步,性能优化

    通过POI读取需要导入的Excel; 以文件名为表名、列头为列名、并将数据拼接成sql; 通过JDBC或mybatis插入数据库; 操作起来,如果文件比较多,数据量都很大的时候,会非常慢。 访问之后,感觉没什么反应,实际上已经在读取 + 入库了,只是比较慢而已。 读取一个10万行的E

    2024年01月23日
    浏览(36)
  • 【Java】Java中使用HashMap优化多层for循环嵌套以及for循环之性能优化

    for循环是开发时常用的语法之一,比如对数组,集合的遍历等,但是如果使用不好也会出现很多新能损耗的问题,今天就来讲解一下for循环的常用性能优化问题。 for循环 里面还有 for循环, 然后做一些数据匹配、处理 这种场景。 m层嵌套的n次的for循环的时间复杂度为O(n^m),

    2024年02月16日
    浏览(32)
  • 【Java 基础篇】深入理解Java HashMap:使用注意事项和性能优化

    Java是一种广泛使用的编程语言,而集合是Java编程中不可或缺的一部分。在Java的集合框架中,HashMap是一个常用的数据结构,用于存储键值对。本文将深入介绍HashMap集合,从基础到高级用法,帮助您更好地理解和利用它。 HashMap是Java集合框架中的一个类,它实现了 Map 接口,用

    2024年02月06日
    浏览(33)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包