代码之家  ›  专栏  ›  技术社区  ›  rcantrel

ScheduledThreadPoolExecutor计划清除内存泄漏

  •  0
  • rcantrel  · 技术社区  · 7 年前

    我正在尝试编写一个封闭的类,从网络上下载每30秒刷新一次的图像。我可能想下载1张图片,或者我可能想下载N张图片。我可能想随时停止下载某个图像。我写了下面的类,除了当我停止下载一个图像内存没有被释放的任务,它工作得很好。或者,如果我停止下载所有图像,内存不会被释放(这在生产中不会发生)。我尝试了几种不同的方法来实现这一点。我最后一次尝试是每隔30秒使用同一个执行器或使用单独执行器下面的代码从ScheduledThreadPoolExecutor中清除任务。我还提供了测试释放内存的代码,虽然我的示例阻止了所有图像的下载,但在实际使用中,我应该只能停止一个图像,并从该任务中释放内存。

    import java.awt.image.BufferedImage;
    import java.io.IOException;
    import java.io.InputStream;
    import java.net.MalformedURLException;
    import java.net.URL;
    import java.net.URLConnection;
    import java.net.URLStreamHandler;
    import java.util.concurrent.ScheduledFuture;
    import java.util.concurrent.ScheduledThreadPoolExecutor;
    import java.util.concurrent.TimeUnit;
    import java.util.function.Consumer;
    
    import javax.imageio.IIOException;
    import javax.imageio.ImageIO;
    import javax.imageio.stream.ImageInputStream;
    
    
    public class ImageLoadTask implements Runnable {
    
        private static ScheduledThreadPoolExecutor taskExecutorService = new ScheduledThreadPoolExecutor(500);
        private static ScheduledThreadPoolExecutor purgeExecutorService = new ScheduledThreadPoolExecutor(500);
        private static Runnable purgeRunnable = () -> purge();
        private ScheduledFuture<?> scheduledFuture;
        private URL pictureURL;
        private Consumer<BufferedImage> successMethod;
        private Consumer<String> failMethod;
        private ImageURLStreamHandler streamHandler = new ImageURLStreamHandler();
    
        private boolean displaySuccess = false;
        private boolean displayError = false;
        private boolean isCancelled = false;
    
        static {
            taskExecutorService.setExecuteExistingDelayedTasksAfterShutdownPolicy(false);
            taskExecutorService.setContinueExistingPeriodicTasksAfterShutdownPolicy(false);
            taskExecutorService.setRemoveOnCancelPolicy(true);
    
            purgeExecutorService.scheduleWithFixedDelay(purgeRunnable, 30L, 30L, TimeUnit.SECONDS);
        }
    
        public ImageLoadTask(String url) {
            try {
                this.pictureURL = new URL(url);
            } catch (MalformedURLException e) {
                if(failMethod != null) {
                    failMethod.accept(e.getMessage()); ;
                }
                if(displayError) {
                    System.out.println("(ImageLoadTask) URL is malformed: " + url+"\n"+ e.getMessage());
                }
            }
        }
    
        public ImageLoadTask(String url, Consumer<BufferedImage> successMethod) {
            this(url);
            this.successMethod = successMethod;
        }
    
        public ImageLoadTask(String url, Consumer<BufferedImage> successMethod, Consumer<String> failMethod) {
            this(url, successMethod);
            this.failMethod = failMethod;
        }
    
        public void start() {
            scheduledFuture = taskExecutorService.scheduleAtFixedRate(this, 0L, 30L, TimeUnit.SECONDS);
        }
    
        public void stop() {
            if(isCancelled)
                return;
    
            isCancelled = true;
            scheduledFuture.cancel(true);
            scheduledFuture = null;
            pictureURL = null;
            successMethod = null;
            failMethod = null;
            streamHandler = null;
    
            taskExecutorService.remove(this);
            taskExecutorService.purge();
        }
    
        public static void purge() {
            System.out.println("Purging");
            taskExecutorService.purge();
        }
    
        @Override
        public void run() {
            if(!isCancelled) {
                try {
                    BufferedImage image = loadImage(pictureURL);
                    if(displaySuccess) {
                        System.out.println("Image received for url " + pictureURL);
                    }
                    if(successMethod != null && !isCancelled) {
                        successMethod.accept(image); ;
                    }
                } catch (IOException e) {
                    if(failMethod != null && !isCancelled) {
                        failMethod.accept(e.getMessage());
                    }
                    if(displayError) {
                        System.out.println("Error occured retrieving image for url: " + pictureURL +"\n"+ e.getMessage());
                    }
                }
            }
        }
    
        public void displayError(boolean displayError) {
            this.displayError = displayError;
        }
    
        public void displaySuccess(boolean displaySuccess) {
            this.displaySuccess = displaySuccess;
        }
    
        private BufferedImage loadImage(URL input) throws IOException {
            if (input == null) {
                throw new IllegalArgumentException("input == null!");
            }
    
            InputStream istream = null;
            try {
                istream = streamHandler.openConnection(input).getInputStream();
            } catch (IOException e) {
                throw new IIOException("Can't get input stream from URL!", e);
            }
            ImageInputStream stream = ImageIO.createImageInputStream(istream);
            BufferedImage bi;
            try {
                bi = ImageIO.read(stream);
                if (bi == null) {
                    stream.close();
                }
            } finally {
                istream.close();
            }
            return bi;
        }
    
        @Override
        protected void finalize() throws Throwable {
            super.finalize();
            System.out.println("finalize");
        }
    
        class ImageURLStreamHandler extends URLStreamHandler {
    
            @Override
            protected URLConnection openConnection(URL url) throws IOException {
                URL target = new URL(url.toString());
                URLConnection connection = target.openConnection();
                // Connection settings
                connection.setConnectTimeout(60000); // 1 min
                connection.setReadTimeout(60000); // 1 min
                return connection;
            }
        }
    }
    

    测试应用程序:

    import java.awt.EventQueue;
    import java.awt.event.ActionEvent;
    import java.awt.event.ActionListener;
    import java.awt.image.BufferedImage;
    import java.util.ArrayList;
    import java.util.List;
    
    import javax.swing.JButton;
    import javax.swing.JFrame;
    import javax.swing.JPanel;
    
    public class ImageLoadTaskTest {
    
        public static void main(String[] args) {
            EventQueue.invokeLater(new Runnable() {
                @Override
                public void run() {
                    Gui gui = new Gui();
                }
            });
        }
    
        static class Gui extends JFrame {
            private static final long serialVersionUID = 1L;
    
            private List<ImageLoadTask> tasks = new ArrayList<>();
            private boolean running = false;
    
            private JButton startStopButton = new JButton("Start");
            private JButton purgeButton = new JButton("Purge");
    
            private ActionListener startStopListener = new ActionListener() {
                @Override
                public void actionPerformed(ActionEvent e) {
                    if(running) {
                        stopTasks();
                    } else {
                        startTasks();
                    }
                }
            };
    
    
            private ActionListener purgeListener = new ActionListener() {
                @Override
                public void actionPerformed(ActionEvent e) {
                    ImageLoadTask.purge();
                }
            };
    
            public Gui() {
                setTitle("Image Load Task Test");
                setBounds(250, 250, 300, 150); // Size
                setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    
                JPanel contentPanel = new JPanel();
                setContentPane(contentPanel);
    
                startStopButton.addActionListener(startStopListener);
                contentPanel.add(startStopButton);
    
    
                purgeButton.addActionListener(purgeListener);
                contentPanel.add(purgeButton);
    
                setVisible(true);
            }
    
            private void startTasks() {
                running = true;
                System.out.println("Starting tasks");
                for(int i = 0; i < 2500; i++) {
                    ImageLoadTask task = new ImageLoadTask("http://placehold.it/120x120&text=image" + i, this::success, this::fail);
                    task.start();
                    tasks.add(task);
                }
                startStopButton.setText("Stop");
            }
    
            private void stopTasks() {
                running = false;
                System.out.println("Stopping " + tasks.size() + " tasks");
                for(ImageLoadTask task : tasks) {
                    task.stop();
                }
                tasks.clear();
                startStopButton.setText("Start");
                System.out.println("Stopped tasks ");
                //ImageLoadTask.purge();
            }
    
            private void success(BufferedImage image) {
                //System.out.println("Success!");
            }
    
            private void fail(String message) {
                //System.out.println("Fail! "+ message);
            }
        }
    }
    
    1 回复  |  直到 7 年前
        1
  •  0
  •   Alexander    7 年前

    当你中断时,你不会关闭你的“流” ImageIO.read(stream)