|
Java форум JavaTalks форум программистов
|
|
|
|
| Предыдущая тема :: Следующая тема |
| Автор |
Сообщение |
OneHalf : 103 Новичок
|
Июн 22, 2011 22:06 |
|
|
Какая разница, что ставить слушателем: внутренний класс или внешний объект?
Если в качестве слушателя поставить внешний класс, реализующий ActionListener, то сборщик мусора точно также ничего не соберет, ведь ссылка на внешний класс все равно существует! Просто тогда существует она не во внутреннем анонимном классе, а в объекте, в котором регистрируются слушатели. Т.е. тут ничего принципиально не меняется - хотите чтобы внешний класс уничтожился сборщиком - удаляйте слушателей.
Хотя, возможно, вы имеете ввиду случай, когда слушателю не нужен доступ к переменным внешнего класса. Тогда действительно имеет смысл использовать статические вложенные классы, или вообще делать их не внутренними, а вполне самостоятельными. |
|
|
|
 |
XoJIoD : 459 Бывалый
|
Июн 22, 2011 22:21 |
|
|
Если мы добавим в слушатели экземпляр внешнего класса, в котором нет ссылки на объект, его создающий (например, с пустой реализацией actionPerformed, как в примере), то ссылка на объект, создающий ActionListener'a ниоткуда сама по себе не появится, соответственно утечки не будет
UPD: прошу прощения, не дочитал Ваш пост, вы говорили о том же |
|
|
|
 |
sgdread : 2184 JT Библиотекарь Откуда: USA
|
Июн 23, 2011 2:06 |
|
|
Так, беседа заходит в тупик. Вот вам код с утечкой памяти, вызванной применением анонимных и не-статических внутренних классов.
| Код: |
| public interface Result {} |
| Код: |
public class OuterClass {
int[] data = null;
public OuterClass(int s) {
data = new int[s];
}
public Result getResult() {
return new Result() { // <-- Утечка Outer класса со всеми потрохами наружу!!!
};
}
} |
Запускаем хозяйство выше следующим кодом:
| Код: |
public static void main(String[] args) {
List<Result> l = new ArrayList<Result>();
int i = 0;
while (true) {
l.add(new OuterClass(10000).getResult());
if (i % 10000 == 0) {
System.out.println(i);
}
i++;
}
} |
У меня валится в OutOfMemoryError на 40000+. Чтобы показать, что проблема не в GC, просто сделаем имплементацию КОНКРЕТНОГО класса (только в отдельном файлике или в статическом Inner-класе плиз, а то нестатические имеют ту же проблему, что и анонимные):
| Код: |
| class ResultImpl implements Result {} |
и модифицируем OuterClass:
| Код: |
public class OuterClass {
int[] data = null;
public OuterClass(int s) {
data = new int[s];
}
public Result getResult() {
return new ResultImpl(); // <- нет утечек!!!
}
} |
Во втором случае мы видим, что приложение жизнерадостно бегает, периодически стопорясь на массивные сборки GC.
Я почему про Swing завел разговор, потому как это довольно распространенная утечка. Конкретных примеров не дам, т. к. нету под рукой, а писать лень.
Эти грабли особенно актуальны на платформах с ограниченной памятью: к примеру на Android. _________________
 |
|
|
|
 |
Skipy : 4801 Я тут живу! Откуда: Москва, Россия
|
Июн 23, 2011 8:44 |
|
|
| sgdread писал(а): |
| Ну вы бы почитали сначала, что я написал. Даже если вы уберете слушатели через removeListener, утечка останется. Причина не в добавлении/удалении, а в том, что анонимный inner-класс неявно содержит ссылку на объект-родитель (возьмите декомпилируйте код с inner-классом и увидите своими глазами - в анонимный класс передается this объекта-родителя). В результате, если у вас есть кнопочка, которая создает новое модальное окно, слушатель на которой - анонимный класс, вы получити феерическую утечку памяти - все модальные окна, созданные таким ActionListener-ом будут храниться в памяти и не будут собраны, пока не будет собран компонент с кнопочкой. Проблема ИМЕННО в анонимных inner-классах. |
Я почитал, и внимательно. Только вот вопрос - нафига в модальном окне ставить слушателя на кнопку в основном фрейме?
Если слушатель на кнопке в модальном окне - весь граф будет убран сборщиком. Если Вы прилагаете усилия, чтобы прикопать ссылку на анонимный класс, который, в свою очередь, держит модальное окно, - ну, извините, Вы сами себе злобный Буратино.
В общем, моё резюме. Проблема не в нестатических иннер-классах как таковых, а в непонимании принципов их устройства и, соответственно, в некорректном их использовании.
P.S. Кстати, я не понял вот этого:
| Цитата: |
| В результате, если у вас есть кнопочка, которая создает новое модальное окно, слушатель на которой - анонимный класс, вы получите феерическую утечку памяти - все модальные окна, созданные таким ActionListener-ом будут храниться в памяти и не будут собраны, пока не будет собран компонент с кнопочкой. Проблема ИМЕННО в анонимных inner-классах. |
Где будет храниться твердая ссылка на созданное модальное окно, если его экземпляр создается в методе и, соответственно, память под ссылку на него выделяется только на фрейме вызова и исключительно на время работы метода? _________________ С уважением,
Евгений aka Skipy
www.skipy.ru
P.S. Я НЕ решаю задачи ЗА других! |
|
|
|
 |
sgdread : 2184 JT Библиотекарь Откуда: USA
|
Июн 23, 2011 19:09 |
|
|
| Skipy писал(а): |
| В общем, моё резюме. Проблема не в нестатических иннер-классах как таковых, а в непонимании принципов их устройства и, соответственно, в некорректном их использовании. |
Верно! И наше обсуждение прямо подтверждает этот факт. Пример утечки см. выше. _________________
 |
|
|
|
 |
initmax : 165 Новичок Откуда: Moon
|
Июн 24, 2011 8:52 |
|
|
| Vermut писал(а): |
| sgdread писал(а): |
| В результате, если у вас есть кнопочка, которая создает новое модальное окно, слушатель на которой - анонимный класс, вы получити феерическую утечку памяти - все модальные окна, созданные таким ActionListener-ом будут храниться в памяти и не будут собраны, пока не будет собран компонент с кнопочкой. Проблема ИМЕННО в анонимных inner-классах. |
Что помешает собирать сборщику мусора создаваемые в листенере окна, ведь он же запоминает ссылку не на них, а на родительский объект? |
Дело в том что инер класс хоть и инер но всё же класс, и он содержит линк на объект в который вложен. GC учищает только те объекты на которых нет ни одной ссылки, вот и происходит засорение кучи. _________________ Пишу дипломы, курсовые, лабары. Консультирую по скайпу до полного прозрения. Обращайтесь в личку. |
|
|
|
 |
Vermut : 1062 Завсегдатай Откуда: Ростов-на-Дону
|
Июн 24, 2011 19:58 |
|
|
Спасибо кэп. Только вопрос был про то, что помешает собирать объекты на которые листенер не хранит ссылки, а только является инициатором их порождения. _________________ Познакомлюсь с привлекательной Ростовчанкой для совместного изучения Java |
|
|
|
 |
stolzen : 422 Бывалый Откуда: Нижний Новгород
|
Июн 25, 2011 12:34 |
|
|
Так значит все-таки как нужно правильно реализовывать ActionListener классы?
| Код: |
public class SomeClass {
static public void main(String[] args) {
JButton b = new JButton();
// первый вариант
b.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
System.out.println("Some Action");
}
});
// второй вариант
b.addActionListener(new AL());
b.doClick();
}
// второй вариант
static class AL implements ActionListener {
@Override
public void actionPerformed(ActionEvent e) {
System.out.println("Another Action");
}
}
} |
Вторым вариантом что ли? |
|
|
|
 |
OneHalf : 103 Новичок
|
Июн 25, 2011 12:52 |
|
|
Кстати, а анонимный класс, созданный из static метода, случайно не является статическим?
Upd: Является. Значит оба варианта в посте выше эквивалентны.
Но в общем ответ прост: если не требуется доступ к нестатическим полям и методам внешнего класса - используйте статические, требуется - нестатические.
Статичность анонимных классов определяется блоками, в которых они объявлены |
|
|
|
 |
Bandito2000 : 7 Новичок
|
Авг 09, 2011 12:44 |
|
|
| sgdread писал(а): |
Так, беседа заходит в тупик. Вот вам код с утечкой памяти, вызванной применением анонимных и не-статических внутренних классов.
| Код: |
| public interface Result {} |
| Код: |
public class OuterClass {
int[] data = null;
public OuterClass(int s) {
data = new int[s];
}
public Result getResult() {
return new Result() { // <-- Утечка Outer класса со всеми потрохами наружу!!!
};
}
} |
Запускаем хозяйство выше следующим кодом:
| Код: |
public static void main(String[] args) {
List<Result> l = new ArrayList<Result>();
int i = 0;
while (true) {
l.add(new OuterClass(10000).getResult());
if (i % 10000 == 0) {
System.out.println(i);
}
i++;
}
} |
У меня валится в OutOfMemoryError на 40000+. Чтобы показать, что проблема не в GC, просто сделаем имплементацию КОНКРЕТНОГО класса (только в отдельном файлике или в статическом Inner-класе плиз, а то нестатические имеют ту же проблему, что и анонимные):
| Код: |
| class ResultImpl implements Result {} |
и модифицируем OuterClass:
| Код: |
public class OuterClass {
int[] data = null;
public OuterClass(int s) {
data = new int[s];
}
public Result getResult() {
return new ResultImpl(); // <- нет утечек!!!
}
} |
Во втором случае мы видим, что приложение жизнерадостно бегает, периодически стопорясь на массивные сборки GC.
Я почему про Swing завел разговор, потому как это довольно распространенная утечка. Конкретных примеров не дам, т. к. нету под рукой, а писать лень.
Эти грабли особенно актуальны на платформах с ограниченной памятью: к примеру на Android. |
Всем привет.
Не знаю может эта тема уже никому не интересна, но я всё же напишу своё мнение насчёт этого примера.
В даном примере, так называемая утечка памяти будет и в первом и во втором случае. Просто в первом случае у нас метод getResult создаёт и возвращает сыллку на объёкт (экземпляр) анонимного класса, который реализует итерфейс Result. Во втором случае метод getResult создаёт объект не анонимного класса ResultImpl, который реализует итерфейс Result. Я хочу сказать что и в первом и втором случае метод getResult создаёт в памяти новый объект и возвращает ссылку на него, которая в свою очередь записывается в List (l). И соответсвенно пока у нас будут ссылки на эти объекты в листе то никакой GC не будет их удалять из памяти.
Считаю данный пример не коректным. Если я не прав поправьте меня. |
|
|
|
 |
XoJIoD : 459 Бывалый
|
Авг 09, 2011 13:00 |
|
|
| Не правы. Под утечкой имелось в виду не то, что список будет держать кучу объектов, а то, что в первом случае каждый объект из этой кучи ещё и будет иметь неявную ссылку на объект, его породивший, т.е. на Outer. Поэтому GC не сможет собрать не только Result'ы (т.к. ссылки на них находятся в списке), но и Outer'ы (т.к. ссылки на них находятся в Result'ах). Во втором случае Result не содержит ссылки на Outer и GC может их (Outer'ы) спокойно собирать |
|
|
|
 |
Bandito2000 : 7 Новичок
|
Авг 09, 2011 13:04 |
|
|
| Всё, теперь понял. Спасибо за разъяснение. |
|
|
|
 |
|
|
Страница 2 из 2 На страницу Пред. 1, 2 |
Список форумов
-> Разное |
|
Вы не можете начинать темы Вы не можете отвечать на сообщения Вы не можете редактировать свои сообщения Вы не можете удалять свои сообщения Вы не можете голосовать в опросах
|
|