[java] Про сборку мусора

olga1969

И опять пример из Брюса Экеля. Про сборку мусора. Вот код
 
class Book {
boolean checkedOut = false;
Book(boolean checkOut) {
checkedOut = checkOut;
}
void checkIn {
checkedOut = false;
}
public void finalize {
if(checkedOut)
System.out.println("Error: checked out");
}
}

public class TerminationCondition {
public static void main(String[] args) {
Book novel = new Book(true);
// Proper cleanup:
novel.checkIn;
// Drop the reference, forget to clean up:
new Book(true);
// Force garbage collection & finalization:
System.gc;
}
}

С этим все ясно. В общем, насильственная сборка мусора.
Поменяем немного main:
 
public static void main(String[] args) {
Book novel = new Book(true);
{
Book novel1 = new Book(true);
}
System.gc;
}


В общем, по идее, должен выдаваться Error: checked out на объект, создаваемый во внутреннем блоке, поскольку он становится "висячим", так как novel1 выходит из области видимости, когда вызывается gc.
Если написать
 
public static void main(String[] args) {
Book novel = new Book(true);
{
Book novel1 = new Book(true);
}
{
Book novel2 = new Book(true);
}
System.gc;
}


то выдается Error: checked out только на один внутренний объект (на который указывала novel1).
Соответственно, если поставить три внутренних блока, то Error выдастся только на первые два.
Куда последный внутренний объект девается? Почему gc его не "видит", не распознает как мусор?

gopnik1994

попробуй вызвать gc 2-3 разя подряд.
Может он его не собирает в первый проход...

olga1969

не, не помогает
Да, вот еще, забыл написать.
Если после внутренних блоков написать что-то типа
 int i =1;  
то все становится в порядке.
То есть такой вот код
  
public static void main(String[] args) {
Book novel1 = new Book(true);
{
Book novel2 = new Book(true);
}
{
Book novel2 = new Book(true);
}

int i = 1;
System.gc;
}

выдает то, что ожидается: два Error'a
Я еще пробовал несколько уровней вложенности блоков, так там еще хитрее получается.
Я добавил еще в класс Book поле int book_id, чтоб отслеживать, какие именно объекты удаляются.
В общем, такое ощущение, что дело в стеке. Я не знаю, как это все реализовано, в смысле, что происходит со стеком при вложенных блоках.
Погоняйте, кому не лень, разные варианты, особенно со вложенными блоками. Может, кто еще что найдет.

enochka1145

Объяснение (отмазка) у меня такое.
} переводится в, грубо говоря, mov ebp, esp. В смысле, стек поднимается на несколько слов (которые были нужны, чтобы разместить ссылки (указатели) на экземпляры Book в куче). И пока ты не затёр эти ссылки своим int i; (т. е. sub esp, 4 для gc они всё ещё легитимны, как ни странно.
Советую поварьировать количество Book-ов и количество int-ов, чтобы убедиться.
Кстати, повторный вызов System.gc никакого эффекта не даёт.

olga1969

И пока ты не затёр эти ссылки своим int i; (т. е. sub esp, 4 для gc они всё ещё легитимны, как ни странно.
Да, я про это подумал. Типа "ленится"?
Советую поварьировать количество Book-ов и количество int-ов, чтобы убедиться.
Можно по-подробнее? У меня, например, int i=1 работает на любом числе Book-ов.

enochka1145

Не знаю. У Экеля перечисляется штуки 4 подхода к сборке мусора. Можно погадать, почему так, но что-то влом. Может, из объекта в куче есть обратная ссылка на указатель стека. Ну, как две части разорванной купюры и для уверенности нужно, чтобы тот указатель (который в стеке) должен испортиться. ХЗ.
> Можно по-подробнее? У меня, например, int i=1 работает на любом числе Book-ов.
class Book {
int i = 0;
Book(int i) { this.i = i; }
public void finalize {
System.out.println("finalize " + i);
}
}

public class Test {
public static void main(String[] args)
{
Book novel = new Book(1);
{
Book n1 = new Book(2);
Book n2 = new Book(3);
Book n3 = new Book(4);
}
int i = 1;
//int j = 2;
//int k = 6;
System.gc;
}
}

bastii

Может, из объекта в куче есть обратная ссылка на указатель стека
Как это?

enochka1145

Грубо говоря:

MyClass obj = new MyClass;
и в области куче, в которой расположен этот экземпляр MyClass, есть компонента - указатель на obj (так сказать, &obj). Но это всё из области догадок.

bastii

Фантазер
Объект ведь дольще живет, чем этот фрейм стэка (так сказать).

enochka1145

О том и разговор зашёл. Оказывается, если предыдущий фрейм стека ещё не испорчен, то для сборщика мусора соответствующий объект сборке мусора не подлежит, хотя формально должен.
Попробуй сам.

psm-home

В общем, похоже, что всё так и есть и на сайте Сана про это написано. web page
NOTE: The im object is set to null because there is no gurantee the stack slot occupied by it will be cleared when it goes out of scope. A later method invocation whose stack frame contains the slot previously occupied by im might not put a new value there, in which case, the garbage collector still considers the slot to contain a root.
This is a problem even with non-conservative exact collectors. As a precaution, you can increase the probability that an object will become softly, weakly, finalizable, or phantomly reachable by clearing variables that refer to it.
Там рядом с NOTE рассматривают мягкие ссылки, но это, похоже, применимо и для обычных.

olga1969

О, спасибо за ссылку.
Да, похоже, вопрос снят.

enochka1145

А вот как дипломатично говорит всё о том же Б. Эккель в своей "Thinking in Java" (Chapter 15: Discovering Problems > Profiling and optimizing > Optimization guidelines):
"Whenever possible try to explicitly discard an instance by setting it to null. This can sometimes be a useful hint to the garbage collector."
Прям как сговорились все.
Оставить комментарий
Имя или ник:
Комментарий: