Java - BufferedImage导致高内存使用率被多个线程加剧?

时间:2016-07-12 08:01:32

标签: java memory bufferedimage

这个问题有许多相似的主题,但我还没有找到解决方案。基本上,我所做的是异步更新ArrayList。然而,这并不是真正相关的,因为无论是否同步或异步运行都会发生高内存使用,但是当我同步运行时,它比不同步运行时更重要(仍然是重要的imo)(可能是因为BufferedImage)存储在多个线程上,而不是多次在单个线程上覆盖。)

这是我为异步部分所做的工作。忍受我,第一次我做过异步任务,所以它可能很糟糕,但它会运行。

public void updateAllShows() {
    // I find it runs faster even when thread amount > # of threads from CPU
    ExecutorService ex = Executors.newFixedThreadPool(array size)

    // All the Future tasks we execute later
    ArrayList<Future<MyShowComp>> tasks = new ArrayList

    // This holds the new MyShowComp that all the Futures calculate
    ArrayList<MyShowComp> tempArray = new ArrayList

    for (MyShowComp comp : array) {
        final MyShowComp c = comp;
        FutureTask<?> t = new FutureTask(new Runnable() {
            @Override
            public void run() {
                // I'll detail this method in a moment
                tempArray.add(updateShowAsync(c,false));
            }
        }, null);
        // Now that future task is created, we add it to the list of tasks
        tasks.add(t);
    }

    // Cycle through and execute every stored task
    for (FutureTasks)
        executor.execute(task);

    executor.shutdown();
    if (shutdown finished)
        array = tempArray;
        // Do some sorting and add to panel
}

那很简单。现在麻烦的部分,我更新了节目的图像:

public MyShowComp updateShowAsync(MyShowComp c, boolean bool) {
    //
    // Bunch of irrelevant code, already tested, doesn't affect memory usage
    //

    BufferedImage img = null;

    // We pull the image from particular URL
    img = ImageIO.read(url);

    // Resize the image accordingly (this method makes little to no difference in memory)
    img = resize(img);

    return new MyShowComp(img);
}

为什么BufferedImage img总是保存在内存中,即使它被完全取消引用?如果我将MyShowComp与完全独立的&amp; null BufferedImage,问题仍然存在。原始的BufferedImage img仍保留在内存中,并且根本不会消失。只有当我注释掉img = ImageIO.read并完全跳过它时,它才会修复。我尝试过img.flush(),img = null等等,没有任何作用。有没有办法强制完全取消引用,以便GC能够完成它的工作?

-----编辑------

这是一些快速测试代码,以便任何想要的人都可以自己测试。

FrameTest.java

import java.awt.EventQueue;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.image.BufferedImage;
import java.net.URL;
import java.util.ArrayList;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.FutureTask;
import java.util.concurrent.TimeUnit;

import javax.swing.JFrame;
import javax.swing.JOptionPane;
import javax.imageio.ImageIO;
import javax.swing.ImageIcon;
import javax.swing.JButton;
import java.awt.BorderLayout;

public class FrameTest {

    private JFrame frame;
    private ArrayList<MyShowComp> array;

    /**
     * Launch the application.
     */
    public static void main(String[] args) {
        EventQueue.invokeLater(new Runnable() {
            @Override
            public void run() {
                try {
                    FrameTest window = new FrameTest();
                    window.frame.setVisible(true);
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        });
    }

    /**
     * Create the application.
     */
    public FrameTest() {
        initialize();
    }

    /**
     * Initialize the contents of the frame.
     */
    private void initialize() {
        frame = new JFrame();
        frame.setBounds(100, 100, 450, 300);
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

        String quick = "Quick";
        array = new ArrayList<>();
        for (int i=0;i<21;i++)
            array.add(new MyShowComp(quick, quick, quick, null));

        JButton btnUpdateShows = new JButton("Update Shows");
        btnUpdateShows.addActionListener(new ActionListener() {

            @Override
            public void actionPerformed(ActionEvent e) {
                updateAllShows();               
            }
        });
        frame.getContentPane().add(btnUpdateShows, BorderLayout.NORTH);
        //JOptionPane.showMessageDialog(frame.getContentPane(), "Click the update button and watch the program's memory usage everytime you do. It'll progressively get higher.");
    }

    private void updateAllShows() {
        // Async method version 1
        /*ExecutorService executor = Executors.newFixedThreadPool(array.size());
        ArrayList<FutureTask<?>> tasks = new ArrayList<>();
        ArrayList<MyShowComp> temp = new ArrayList<>();

        for (MyShowComp myShowComp : array) {
            final MyShowComp c = myShowComp;
            FutureTask<?> newTask= new FutureTask<Object>(new Runnable() {
                @Override
                public void run() {
                    temp.add(updateShow(c, false)); 
                }
            }, null);
            tasks.add(newTask);
        }
        for (FutureTask<?> futureTask : tasks) 
            executor.execute(futureTask);

        executor.shutdown();
        try {
            boolean finished = executor.awaitTermination(1, TimeUnit.MINUTES);
            if (finished) {
                array = temp;
            }
        } catch (InterruptedException e) {
            System.out.println("Asynch thread interrupted.");
            e.printStackTrace();
        }*/

        // Async method version 2
        ExecutorService executor = Executors.newFixedThreadPool(array.size());
        class NewTask implements Callable<MyShowComp> {
            MyShowComp c;
            public NewTask(MyShowComp c) {
                this.c = c;
            }

            public MyShowComp call() {
                return updateShow(c, false);
            }
        }
        ArrayList<Future<MyShowComp>> taskList = new ArrayList<>();
        for (MyShowComp MyShowComp : array) 
            taskList.add(executor.submit(new NewTask(MyShowComp)));

        ArrayList<MyShowComp> temp = new ArrayList<>();
        for (Future<MyShowComp> future : taskList) {
            try {
                temp.add(future.get());
            } catch (InterruptedException e) {
                e.printStackTrace();
            } catch (ExecutionException e) {
                e.printStackTrace();
            }
        }
        executor.shutdown();
        taskList = new ArrayList<>();
        array = temp;
        temp = new ArrayList<>();

        // Synchronous method
        /*ArrayList<MyShowComp> tempTwo = new ArrayList<>();
        for (MyShowComp myShowComp: array) 
            tempTwo.add(updateShow(myShowComp, false));         

        array = tempTwo;*/

    }

    private MyShowComp updateShow(MyShowComp c, boolean b) {

        BufferedImage img = null;
        // Load the images
        try {
            // Random Game of Thrones image from IMDB
            URL url = new URL("http://ia.media-imdb.com/images/M/MV5BMjM5OTQ1MTY5Nl5BMl5BanBnXkFtZTgwMjM3NzMxODE@._V1_SY1000_CR0,0,674,1000_AL_.jpg");
            img = ImageIO.read(url);
        } catch (Exception e) {
            e.printStackTrace();
        }

        //MyShowComp newComp = new MyShowComp("lol", "Leaks?", "Probably", new ImageIcon(img));
        MyShowComp newComp = new MyShowComp("lol", "Leaks?", "Probably", new ImageIcon(img));
        //System.out.println(newComp);
        img.flush();
        img = null;
        System.gc();
        return newComp;
    }
}

MyShowComp.java

import javax.swing.Icon;

public class MyShowComp {
    private String test, testTwo, testThree;
    private Icon icon;
    public MyShowComp(String test, String testTwo, String testThree, Icon icon) {
        this.test = test;
        this.testTwo = testTwo;
        this.testThree = testThree;
        this.icon = icon;
    }

    @Override
    public String toString() {
        return test + "\n" + testTwo + "\n" + testThree + "\n";
    }
}

0 个答案:

没有答案