怎么优化Java编程中的性能

发布时间:2021-01-06 16:45:52 作者:Leah
来源:亿速云 阅读:163

怎么优化Java编程中的性能?相信很多没有经验的人对此束手无策,为此本文总结了问题出现的原因和解决方法,通过这篇文章希望你能解决这个问题。

   String作为我们使用最频繁的一种对象类型,其性能问题是最容易被忽略的。作为Java中重要的数据类型,是内存中占据空间比较大的一个对象。如何高效地使用字符串,可以帮助我们提升系统的整体性能。

  现在,我们就从String对象的实现、特性以及实际使用中的优化这几方面来入手,深入理解以下String的性能优化。

  在这之前,首先看一个问题。通过三种方式创建三个对象,然后依次两两匹配,得出的结果是什么?答案留到最后揭晓。

String str1 = "abc";
String str2 = new String("abc");
String str3 = str2.intern();
System.out.println(str1 == str2);
System.out.println(str2 == str3);
System.out.println(str1 == str3);

  String对象是如何实现的?

  Java中对String对象做了大量的优化,以此来节约内存空间,提升String对象的性能。下图是Java6 -> Java9 String对象属性的变化:

怎么优化Java编程中的性能

  可以看到,String的属性有了以下的变化:

  为什么要这么改呢?

  我们知道,一个char字符占16位,2个字节。这种情况下存储单字节的字符就很容易浪费了。JDK1.9的String类为了节省内存空间,就使用了占8位,1个字节的byte数组来存储字符串。

  coder属性的作用是:在计算字符串长度或者使用indexOf()时,需要根据这个字段,判断如何计算字符串的长度。coder属性值默认有0和1两个值,0代表Latin-1(单字节编码),1代表UTF-16。如果String判断字符串只包含Latin-1,则coder值取0,反之为1。

  String对象的不可变性

  如果看过String的源码,就会发现,String类是被final关键字修饰的,且变量char数组也被final修饰。

  一个类被final修饰代表着该类不可继承,char[]被private和final修饰着,代表String对象不可被更改。这就叫做String对象的不可变性。即如果String对象一旦创建成功了,就不能再对它进行改变。

  这样做的好处在哪里?  

  第一、保证了String对象的安全性。假设String对象是可变的,那么String对象就会被恶意修改。

  第二.、保证hash属性值不会频繁变更,确保了唯一性。使得类似HashMap容器才能实现相应的key-value缓存功能。

  第三、可以实现字符串常量池。Java中,通常有2种创建字符串对象的方式,一种是通过字符串常量的方式创建,如String str = "abc";另一种是字符串常量通过new形式的创建,如String str = new String("abc")。

  当代码中使用第一种方式创建字符串对象时,JVM首先检查该对象是否在字符串常量池中,如果在就返回该对象的引用,否则新的字符串将在常量池中创建。这种方式可以减少同一个值的字符串对象的重复创建,节约内存。

  第二种方式,首先在编译类文件时,"abc"常量字符串将会放入到常量结构中,在类加载时,"abc"会在常量池中创建;然后调用new时,JVM命令将会调用String的构造函数,同时引用常量池的"abc"字符串,在堆内存中创建一个String对象,最后str引用String对象。

  String对象的优化

  1.如何构建超大字符串

  编程过程中字符串的拼接很常见。如果使用String对象相加,拼接我们想要的字符串,会不会产生多个对象呢?比如说以下代码:

String str = "ab" + "cd" + "ef";

  分析代码可知:首先会生成ab对象,再生成abcd对象,最后生成abcdef对象。理论上说,代码很低效。

  但实际上,会发现只有一个对象生成,这是为什么呢?编译时编译器会自动帮我们优化代码,使得最后只得出一个对象“abcdef”。

  再来看看,如果进行字符串常量的累计,又会出现什么结果?

String str = "abcdef";
for (int i = 0; i < 100; i++) {
   str = str + i;
 }

  上面的代码编译后,编译器同样对代码进行了优化,在进行字符串拼接时,偏向使用StringBuilder,这样可以提升效率。上面的代码变成了下面这样:

String str = "abcdef";
for (int i = 0; i < 100; i++) {
  str = (new StringBuilder(String.valueOf(str))).append(i).toString();
}

  总结:即使使用+号作为字符串的拼接,一样可以被编译器优化成StringBuilder的方式。但如果每次循环都生成一个新的StringBuilder实例,同样会降低系统的性能。所以平时做字符串拼接的时候,建议还是显示使用StringBuilder来提升性能。在多线程编程时,String对象的拼接涉及到了线程安全,可以使用StringBuffer。但由于StringBuffer是线程安全的,涉及到锁竞争,所以就性能上来说会比StringBuilder差些。

  2.如何使用String.intern节省内存?

  对于一些数据,数据量非常大,但同时又有大部分重合的,该如何处理呢?

  具体做法是,每次赋值的时候使用String的intern方法,如果常量池中有相同值,就会重复使用该对象,返回对象的引用,这样一开始的对象就可以被回收掉了,这样的话数据量就会大幅度降低了。

  我们再来看一个例子:

String a = new String("abc").intern();
String b = new String("abc").intern(); 
if (a == b) {
   System.out.println("a == b");
}

  输出结果是: a == b

  在字符串常量池中,默认会将对象放入常量池;在字符串变量中,对象总是创建在堆内存的,同时也会在常量池中创建一个字符串对象,复制到堆内存对象中,并返回堆内存对象引用。

  如果调用intern方法,会去查看字符串常量池中是否有等于该对象的字符串,如果没有,就会在常量池中新增该对象,并返回该对象引用;如果有则返回常量池中的字符串引用。堆内存中原有的对象由于没有引用指向它,将会通过垃圾回收器回收。

  3.如何使用字符串的分割方法?

  spilt()方法使用了正则表达式实现了强大的分割功能,而正则表达式的性能是非常不稳定的,使用不当会引起回溯问题,很可能导致CPU居高不下。

  所以要慎重使用spilt方法,我们可以用String.indexOf()方法代替spilt()方法完成字符串的分割,如果实在无法满足需求,就在使用spilt方法时,对回溯问题加以重视就可以了。

看完上述内容,你们掌握怎么优化Java编程中的性能的方法了吗?如果还想学到更多技能或想了解更多相关内容,欢迎关注亿速云行业资讯频道,感谢各位的阅读!

推荐阅读:
  1. 怎么优化golang性能
  2. 如何优化Django中ORM的性能问题

免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。

java ava

上一篇:利用易语言怎么对进程的内存进行优化

下一篇:使用php怎么对时间和内存使用情况进行统计

相关阅读

您好,登录后才能下订单哦!

密码登录
登录注册
其他方式登录
点击 登录注册 即表示同意《亿速云用户服务条款》