0%

性能调优

字符串性能优化

String对象的实现原理在不同的JDK版本是不同的:

  • 在JDK 6以及之前的版本中,String对象主要有四个成员变量:char[] 数组、偏移量 offset、字符数量 count、哈希值 hash。

    String对象是通过offset和count属性通过char[]数组获取字符串,这种方式效率很高,但是有可能会导致内存泄漏。

    因为substring()方法返回的子字符串对象会使用原字符串中的char[]数组,这就会导致原字符串始终无法被回收掉。

  • JDK 7版本对String类做了修改,移除了offset和count这两个属性,substring()方法也不再共享char[]数组,解决了内存泄漏的问题。

  • JDK 9版本将String类中的char[]数组改为了 byte[]数组,新增编码格式属性coder。

    这样做的好处是可以节约内存,因为一个char字符占2个字节,一个byte字符占1个字节。

    coder属性的作用是,在计算字符串长度或者使用indexOf()方法时,需要根据这个字段,判断如何计算字符串长度。

通过 String.intern方法节省内存

调用intern()方法时,会判断字符串常量池中是否有相同的对象,如果没有,就在常量池中新增该对象,如果有,就返回常量池中的对象。

如果对空间要求很高,并且存在大量重复字符串时,可以考虑调用intern()方法。

⭐GC调优

降低Minor GCMinor GC频率

[Minor GC](#Minor GC)只有Eden区耗尽才会触发,如果新生代空间比较小,就会导致[Minor GC](#Minor GC)很频繁,可以通过增大新生代空间降低[Minor GC](#Minor GC)的频率。

扩容Eden区虽然可以减少[Minor GC](#Minor GC)的次数,但是会导致新生代中堆积更多对象,有可能会增加单次[Minor GC](#Minor GC)的时间?

[Minor GC](#Minor GC)主要分为两部分:扫描新生代、将存活对象复制到Survivor区

通常复制对象的成本要远高于扫描成本。

  • 如果内存中大部分对象存活时间都比较长,这种情况增加新生代空间,会导致新生代中堆积更多还活着的对象,[Minor GC](#Minor GC)要复制更多对象,反而会增加[Minor GC](#Minor GC)时长。
  • 如果内存中的对象存活时间都比较短,这种情况扩容新生代,并不会增加单次[Minor GC](#Minor GC)的时间,因为[Minor GC](#Minor GC)时长主要取决于,扫描后存活对象的数量。如果Eden区空间比较大,[Minor GC](#Minor GC)的时间间隔就会变长,大部分对象在[Minor GC](#Minor GC)开始之前就已经死掉了,这样就可以减少复制对象的数量。

降低[Full GC](#Full GC)频率

堆空间不足、老年代空间不足,会触发[Full GC](#Full GC)。

[Full GC](#Full GC)会导致上下文切换,降低系统性能,降低Full GC的频率:

  1. 避免创建大对象,如果对象的大小超过了新生代最大对象阈值,会被直接创建在老年代,
  2. 增大堆空间。