这是Java中的内存泄漏吗?

时间:2017-09-07 13:07:55

标签: java concurrency

在以下代码中: -

Set<Object> allObjects = new HashSet<>(1000);
while(...){ //really big loop - millions
  Object s = getSomeObject();//returns a big object
  allObjects.add(s);

  if(allObject.size() == 1000){
    //If allObjects has got big enough, process and proceed further
    final Set<Object> allObjects_temp = new HashSet<>(allObjects); //--->a
    allObject.clear();

    //Process these objects in a separate tasks parallely
    executerService.submit(new Runnable() {
        public void run() {
            processData(partner, allObjects_temp);//---->b
        }
        }));
  }
}

Line-a 会创建一个新的集合,其中包含复制的元素。

line-b 中,这些元素正在另一个线程中处理。但是,匿名类正在通过Object s访问外部类(allObjects_temp)中的引用,我相信即使在任务完成之后,Java GC也无法收集在循环中创建的对象。

Java GC实际上是否会收集对象?如果愿意,请简要说明原因。如果它无法收集对象,我们如何解决这个问题呢?

参考:When exactly is it leak safe to use (anonymous) inner classes?

更新: 编写以下代码以验证内存泄漏。如果没有内存,代码应该永远运行。

public class MemoryLeak {

    public static void main(String[] args) {
        ExecutorService executerService = new ThreadPoolExecutor(10, 10, 50, TimeUnit.MILLISECONDS,
            new ArrayBlockingQueue<Runnable>(50, true), new ThreadPoolExecutor.CallerRunsPolicy());
        Set<Object> allObjects = new HashSet<>(1500);
        for (int i = 0; i < 100000;) { // really big loop - millions
            Object s = getSomeObject();// returns a big object
            allObjects.add(s);

            if (allObjects.size() == 1500) {
            // If allObjects has got big enough, process and proceed further
            final Set<Object> allObjects_temp = new HashSet<>(allObjects); // --->a
            allObjects.clear();

            // Process these objects in a separate tasks parallely
            executerService.submit(new Runnable() {
                public void run() {
                processData(allObjects_temp);// ---->b
                }
            });
            }
        }
        executerService.shutdown();
    }

    private static Object getSomeObject() {
        return "hello" + (Math.random() * 100000) + "" + (Math.random() * 100000);

    }

    protected static void processData(Set<Object> allObjects_temp) {
        try {
            Runtime rt = Runtime.getRuntime();
            System.err.println(String.format("Free: %d Mb, Total: %d Mb, Max: %d Mb", rt.freeMemory() / 1000000,
                rt.totalMemory() / 1000000, rt.maxMemory() / 1000000));
            Thread.sleep(10);
            System.out.println("done");
        } catch (InterruptedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }

    }
}

结果: 代码永远在运行。我还检查了内存是否经常被释放,Jmap向我展示了Object s确实被垃圾收集了。我还是不完全明白怎么做?

1 个答案:

答案 0 :(得分:1)

我认为上面的代码不会产生内存泄漏。

在热点JVM中,GC将通过可达性分析检查对象是否有效。而静态变量stack变量可以视为imageView = (ImageView)findViewById(R.id.imageView); imageView.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { // Do something here loadImageFromUrl(url); } }); root。

在您的代码中,对象GC变量的引用将存储在JVM堆栈中。将新对象引用分配给allObjects_temp时,JVM堆栈没有保留旧对象allObjects_temp的引用,然后当任务线程运行结束时,这意味着任务线程没有&#39} ; t保持旧对象的引用。因此,旧对象无法访问,GC可以收集它。

请参阅以下代码:

allObjects_temp

第1行的字节代码是:

    Set<String>  set=new HashSet<>();
    while (true){
        String s=new String("test");
        set.add(s);
        if(set.size()==100){
            final Set<String> temp=new HashSet<>(set);   //line 1
            set.clear();;
        }
    }

line2表示最终变量存储在jvm堆栈中。