Утечка памяти с перетаскиванием Swing

У меня есть JFrame, который принимает отбрасывания верхнего уровня файлов. Однако после того, как отбрасывание произошло, ссылки на кадр считаются неограниченно долго в некотором Swing внутренними классами. Я полагаю, что избавление от кадра должно высвободить все свои средства, поэтому что я делаю неправильно?

Пример

import java.awt.datatransfer.DataFlavor;
import java.io.File;
import java.util.List;

import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.TransferHandler;

public class DnDLeakTester extends JFrame {
    public static void main(String[] args) {
        new DnDLeakTester();

        //Prevent main from returning or the jvm will exit
        while (true) {
            try {
                Thread.sleep(10000);
            } catch (InterruptedException e) {

            }
        }
    }
    public DnDLeakTester() {
        super("I'm leaky");

        add(new JLabel("Drop stuff here"));

        setTransferHandler(new TransferHandler() {
            @Override
            public boolean canImport(final TransferSupport support) {
                return (support.isDrop() && support
                        .isDataFlavorSupported(DataFlavor.javaFileListFlavor));
            }

            @Override
            public boolean importData(final TransferSupport support) {
                if (!canImport(support)) {
                    return false;
                }

                try {
                    final List files = (List) 
                            support.getTransferable().getTransferData(DataFlavor.javaFileListFlavor);

                    for (final File f : files) {
                        System.out.println(f.getName());
                    }
                } catch (Exception e) {
                    e.printStackTrace();
                }

                return true;
            }
        });

        setDefaultCloseOperation(DISPOSE_ON_CLOSE);
        pack();
        setVisible(true);
    }
}

Для репродуцирования выполните код и отбросьте некоторые файлы на кадре. Закройте кадр, таким образом, от него избавляются.

Для проверки утечки, я беру дамп "кучи" с помощью JConsole и анализирую его с инструментом Eclipse Memory Analysis. Это показывает этому sun.awt. AppContext содержит ссылку на кадр через его hashmap. Похоже, что TransferSupport виновным.

изображение пути к GC базируется http://img402.imageshack.us/img402/4444/dndleak.png

Что я делаю неправильно? Я должен просить, чтобы код поддержки DnD вымылся так или иначе?

Я выполняю обновление JDK 1.6 19.

7
задан tom 11 May 2010 в 06:49
поделиться

3 ответа

Хотя DropHandler не удаляется из статической карты AppContext, на самом деле это не основная причина, а лишь одна из причин в цепочке. (Обработчик перетаскивания задуман как одноэлементный и не очищается до тех пор, пока класс AppContext не будет выгружен, что на практике никогда не происходит.) Одноэлементный DropHandler используется намеренно.

Настоящая причина утечки заключается в том, что DropHandler устанавливает экземпляр TransferSupport, который повторно используется для каждой операции DnD, и во время операции DnD предоставляет ему ссылку на компонент, задействованный в DnD. Проблема в том, что он не очищает ссылку, когда DnD заканчивает . Если DropHandler вызвал TransferSupport.setDNDVariables (null, null) при выходе из DnD, тогда проблема исчезла бы. Это также наиболее логичное решение, поскольку ссылка на компонент требуется только во время работы DnD. Другие подходы, такие как очистка карты AppContext, позволяют обойти дизайн, а не исправить небольшую оплошность.

Но даже если мы это исправим, кадр все равно не будет собран. К сожалению, возникает другая проблема: когда я закомментировал весь код, связанный с DnD, уменьшив его до простого JFrame, он тоже не собирался. Ссылка на доработку была в javax.swing.BufferStrategyPaintManager . Для этого есть еще не исправленный отчет об ошибке .

Итак, если мы исправим DnD, мы столкнемся с другой проблемой удержания с перерисовкой. К счастью, все эти ошибки касаются только одного кадра (надеюсь, одного и того же!), Так что это не так плохо, как могло бы быть. Фрейм удаляется, поэтому собственные ресурсы освобождаются, и все содержимое может быть удалено, что позволяет освободить его,снижение серьезности утечки памяти.

Итак, чтобы наконец ответить на ваш вопрос, вы не делаете ничего плохого, вы просто уделяете некоторым ошибкам в JDK немного эфирного времени!

ОБНОВЛЕНИЕ: Ошибка диспетчера перерисовки имеет быстрое исправление - добавление

-Dswing.bufferPerWindow=false

в параметры запуска jvm позволяет избежать ошибки. После устранения этой ошибки имеет смысл опубликовать исправление для ошибки DnD:

Чтобы исправить проблему DnD, вы можете добавить вызов этого метода в конце importData ().

            private void cancelDnD(TransferSupport support)
            {
                /*TransferSupport.setDNDVariables(Component component, DropTargetEvent event) 
                Call setDNDVariables(null, null) to free the component.
*/
                try
                {
                    Method m = support.getClass().getDeclaredMethod("setDNDVariables", new Class[] { Component.class, DropTargetEvent.class });
                    m.setAccessible(true);
                    m.invoke(support, null, null);
                    System.out.println("cancelledDnd");
                }
                catch (Exception e)
                {
                }
            }
4
ответ дан 7 December 2019 в 12:16
поделиться

Изменится ли результат, если вы добавите это в свой класс?

@Override
public void dispose()
{
    setTransferHandler(null);
    setDropTarget(null);    // New
    super.dispose();
}

Обновление: Я добавил еще один вызов dispose. Я думаю, что установка цели сброса в null должна освободить еще несколько ссылок. Я не вижу ничего другого в компоненте, что можно было бы использовать, чтобы заставить код DnD отпустить ваш компонент.

1
ответ дан 7 December 2019 в 12:16
поделиться

Изучив источник соответствующих классов, я убежден, что это неизбежно утечка в TransferHandler.

Объект в карте AppContext, DropHandler, никогда не удаляется. Поскольку ключом является объект DropHandler.class, а DropHandler - частный внутренний класс, единственный способ удалить его извне TransferHandler - это очистить всю карту или использовать обман отражения.

DropHandler содержит ссылку на объект TransferSupport, который никогда не очищается. Объект TransferSupport содержит ссылку на компонент (в моем случае JFrame), который также не очищается.

0
ответ дан 7 December 2019 в 12:16
поделиться
Другие вопросы по тегам:

Похожие вопросы: