疯狂java


您现在的位置: 疯狂软件 >> 新闻资讯 >> 正文

读”Java 性能优化之 String 篇“有感


 

  读了 IBM Developer Works上的一篇 Java 性能优化之 String 篇 感觉写得比较实用,但也有一些问题值得斟酌:

  1)关于内存空间的使用大小,虽然Java的基本类型的大小是固定的,不因运行环境而变化,但是Object overhead,以及Reference的大小是与平台相关的,比如在64bit的机器上Object overhead 应该是16 bytes,Reference是8bytes,这点应该说清楚。特别是作者是2012年发表的此文,这时应该64bit的系统比较普及了。

  2)作者提到用如下方法重新构建子串,从原释放原来字符串的空间:

  Java代码

 

  String newString = new String(smallString.toCharArray());

  String newString = new String(smallString.toCharArray());

  个人认为,这个不是个好方法,道先 toCharArray()会拷贝一次char[],但 new String(char[] value) 会再拷贝一次:

  Java代码

 

  public String(char value[]) {

  int size = value.length;

  this.offset = 0;

  this.count = size;

  this.value = Arrays.copyOf(value, size);

  }

  public String(char value[]) {

  int size = value.length;

  this.offset = 0;

  this.count = size;

  this.value = Arrays.copyOf(value, size);

  }

  比较好的方法是:

  Java代码

 

  String newString = new String(smallString);

  String newString = new String(smallString);

  看一下这个构造函数的原代码就知道了:

  Java代码

 

  public String(String original) {

  int size = original.count;

  char[] originalValue = original.value;

  char[] v;

  if (originalValue.length > size) {

  // The array representing the String is bigger than the new

  // String itself. Perhaps this constructor is being called

  // in order to trim the baggage, so make a copy of the array.

  int off = original.offset;

  v = Arrays.copyOfRange(originalValue, off, off+size);

  } else {

  // The array representing the String is the same

  // size as the String, so no point in making a copy.

  v = originalValue;

  }

  this.offset = 0;

  this.count = size;

  this.value = v;

  }

  public String(String original) {

  int size = original.count;

  char[] originalValue = original.value;

  char[] v;

  if (originalValue.length > size) {

  // The array representing the String is bigger than the new

  // String itself. Perhaps this constructor is being called

  // in order to trim the baggage, so make a copy of the array.

  int off = original.offset;

  v = Arrays.copyOfRange(originalValue, off, off+size);

  } else {

  // The array representing the String is the same

  // size as the String, so no point in making a copy.

  v = originalValue;

  }

  this.offset = 0;

  this.count = size;

  this.value = v;

  }

  3) 作者提到:

  使用 String 的 intern()方法返回 JVM 对字符串缓存池里相应已存在的字符串引用,从而解决内存性能问题,但这个方法并不推荐!原因在于:首先,intern() 所使用的池会是 JVM 中一个全局的池,很多情况下我们的程序并不需要如此大作用域的缓存;其次,intern() 所使用的是 JVM heap 中 PermGen 相应的区域,在 JVM 中 PermGen 是用来存放装载类和创建类实例时用到的元数据。程序运行时所使用的内存绝大部分存放在 JVM heap 的其他区域,过多得使用 intern()将导致 PermGen 过度增长而最后返回 OutOfMemoryError,因为垃圾收集器不会对被缓存的 String 做垃圾回收。

  他建议自己建一个 String Cache,但这个Cache也同样是点用了堆的(在new Gen或者Old Gen中),而且何时释放,怎么释放也很重要,如果用强引用也是不会被回收的。而Perm Gen虽然不会被回收,也是可以通过JVM来调节大小的。所以作者的理由不是很充分。我建议可以用Soft Reference来建这个自定义的String Cache。