seman198102
2009-09-12 13:04 阅读 357
已采纳

对象数组回收时可能会内存泄漏,primitive 数组不会吗?

在ArrayList中有clear()方法,如下;就是说如果没有显示调用clear(除arraylist实例是局部变量,生命期结束自动调用),即将所有数组的各个元素置为null,则会导致内存泄露

[code="java"]
private transient Object elementData[];
public void clear() {
modCount++;
// Let gc do its work
for (int i = 0; i < size; i++)
elementData[i] = null;
size = 0;
}
[/code]
那么对于像int[]这样的数组,我们为什么只需要将数组引用置为null就可以了呢,请问有何区别,谢谢!

另外,stringbuffer相比string可以变化,只是因为他在数组长度不够的时候,重新申请新的byte数组的原因,那么Stringbuffer本质上也是会产生很多数组对象的,怎么就说stringbuffer的效率高呢?

  • 点赞
  • 写回答
  • 关注问题
  • 收藏
  • 复制链接分享

2条回答 默认 最新

  • 已采纳
    iteye_7822 iteye_7822 2009-09-13 00:43

    int[]是元素类型为原始类型的数组。原始类型(不包括String)中肯定不含有对对象实例的引用。如果把所有对象实例看做一幅有向图的节点,把它们之间的引用关系看作边,那么int[]肯定是叶节点(有入边没有出边)或者是孤立的节点(入边和出边都没有)。元素为对象类型的数组中则可能包含对别的对象实例的节点。

    [quote="bohemia"]第二个问题:
    StringBuffer比String效率高.是因为生成对象的个数不一样.
    比如:
    [code]"aaa"+"bbb"[/code]
    会生成三个String对象.

    [code]sb.append("aaa").append("bbb")[/code]
    只有2个String生成,所以效率会高.

    个人见解.[/quote]
    确实跟创建对象的个数有关系,不过不应该只看String对象的个数。

    首先用常量写的"aaa"和"bbb"在Java源文件被编译为Java class文件后会出现在常量池中。这些字符串会随着它们所在的class文件的类被加载而被创建为String实例,保存在运行时的字符串池里。再说
    [code]"aaa"+"bbb"[/code]
    不会在这个表达式执行时“生成”3个String对象,而是因为连接运算的两个参数都是字面量,而在Java源码编译为Java class文件时被合并为一个"aaabbb"常量。

    其次,从Java 5开始String的连接运算a+b会变为
    [code]StringBuilder sb = new StringBuilder();
    sb.append(a);
    sb.append(b);
    String result = sb.toString();[/code]
    其实至少创建了一个StringBuilder对象,一个char[](StringBuilder的成员),一个String对象(结果)和又一个char[](String的成员)。
    Java 5以前String的连接运算用的是StringBuffer来做的,跟上面的讲解类似。
    现在的Java编译器已经比较聪明,看到同一个表达式内连续出现连接运算时,会使用同一个StringBuilder来处理:
    [code]a + b + c + d[/code]
    会变为
    [code]StringBuilder sb = new StringBuilder();
    sb.append(a);
    sb.append(b);
    sb.append(c);
    sb.append(d);
    String result = sb.toString();[/code]

    要举例说明String的连接运算比StringBuilder/StringBuffer低效,可以在循环里做连接运算:
    [code]String a = "a";
    for (int i = 0; i < 10; i++) {
    a = a + "b";
    }[/code]
    这个等价于
    [code]String a = "a";
    StringBuilder sb = null;
    for (int i = 0; i < 10; i++) {
    sb = new StringBuilder();
    sb.append(a);
    sb.append("b");
    }
    a = sb.toString();[/code]
    与直接使用StringBuilder的例子相比
    [code]String a = "a";
    StringBuilder sb = new StringBuilder();
    for (int i = 0; i < 10; i++) {
    sb.append("b");
    }
    a = sb.toString();[/code]
    很明显用String的加号的方式创建了更多的StringBuilder对象。但创建StringBuilder对象本身开销并不大,开销大的是其成员的char[]的创建和扩大。直接使用StringBuilder时,连接运算到char[]装不下的时候,会重新分配一个更大的char[],把老数组的内容复制过去。用String的加号时,每次循环都要创建一个新的char[],复制的次数比直接用StringBuilder多很多,因而速度慢。

    点赞 评论 复制链接分享
  • bohemia bohemia 2009-09-12 17:47

    试着回答一下,
    第一个问题:
    [quote]在ArrayList中有clear()方法,如下;就是说如果没有显示调用clear(除arraylist实例是局部变量,生命期结束自动调用),即将所有数组的各个元素置为null,则会导致内存泄露 [/quote]
    不是不调用clear,就一定会导致内存泄露.
    比如如下代码:
    [code="java"]
    main(){
    List a = new ArrayList();
    a.add("111");
    a.add("222");
    }
    [/code]
    当方法执行完后,a对象就被标示未引用状态,引用计数为0的时候,自然会被gc回收;
    显示调用clear的方法作用是,让elementData数组的对象,提前得到释放.
    像你提到的,如果arrayList不是局部变量的时候. 就需要及时的清空.提早释放数组内的内容;

    第二个问题:
    StringBuffer比String效率高.是因为生成对象的个数不一样.
    比如:
    [code="java"]"aaa"+"bbb"[/code]
    会生成三个String对象.

    [code="java"]sb.append("aaa").append("bbb")[/code]
    只有2个String生成,所以效率会高.

    个人见解.

    点赞 评论 复制链接分享

相关推荐