这个问题有许多相似的主题,但我还没有找到解决方案。基本上,我所做的是异步更新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";
}
}