JVM може лесно да актуализира препратките на локални променливи, статични препратки, екземпляри на класове или екземпляри на обектен масив, когато премествате обект в купчина. Но как може да актуализира препратките, изпратени към стека на операндите?
Как Garbage Collector актуализира препратките, изпратени към стека на операндите?
Отговори (1)
Няма фундаментална разлика между локална променлива и запис в стека на операндите. И двете живеят в една и съща стекова рамка. Нито едно от тях не е официално декларирано и и двете се нуждаят от JVM за извършване на изводи, за да разпознаят действителната им употреба.
Следният код
public static void example() {
{
int foo = 42;
}
{
Object bar = "text";
}
{
long x = 100L;
}
{
Object foo, bar = new Object();
}
}
(обикновено) ще се компилира до
public static void example();
Code:
0: bipush 42
2: istore_0
3: ldc #1 // String text
5: astore_0
6: ldc2_w #2 // long 100l
9: lstore_0
10: new #4 // class java/lang/Object
13: dup
14: invokespecial #5 // Method java/lang/Object."<init>":()V
17: astore_1
18: return
Обърнете внимание как локалната променлива с индекс 0
в рамката на стека се преназначава със стойности от различни типове. Като бонус, последното съхраняване на променлива с индекс 1
прави променливата с индекс 0
невалидна, тъй като иначе би съдържала висяща половина от long
стойност.
Няма допълнителни подсказки за типа на локалните променливи, информацията за отстраняване на грешки не е задължителна и таблиците с карта на стека са там само когато кодът съдържа разклонения.
Единственият начин да определите дали дадена локална променлива съдържа препратка е да следвате потока на програмата и да проследите ефекта от инструкциите. Това вече предполага извеждане на стойностите в стека на операндите, тъй като без него дори не бихме знаели какво е поставила инструкцията store
в променливата.
Верификаторът го прави, дори е задължителен, а събирачът на отпадъци или какъвто и да е поддържащ код на JVM също може да го направи. Реализацията може дори да има единичен анализиращ код, който да пази информацията за типа на първия анализ, който би бил проверката.
Но дори когато тази информация се реконструира всеки път, когато събирачът на отпадъци се нуждае от нея, режийните разходи няма да бъдат астрономически. Събирачът на отпадъци работи само периодично и се нуждае само от тази информация за текущо изпълняваните методи. И това е всичко само за интерпретирано изпълнение.
Когато JIT компилаторът генерира код, той така или иначе трябва да използва информацията за типа и може да подготви информация за събирача на отпадъци, но ще го направи само за определени точки, наречени безопасни точки, където генерираният код проверява дали има изключително събиране на боклука. Това означава, че между тези точки не е необходимо данните да бъдат във форма, която събирачът на боклук разбира и оптимизираният код може да приеме, че събирачът на боклук няма да премести обекти, докато ги обработва.
Това също така предполага, че в компилиран, оптимизиран код достижимостта може да е напълно различна от тази в просто интерпретирано изпълнение, т.е. неизползваните променливи може да отсъстват, но дори използваните обекти от гледна точка на изходния код могат да се считат за неизползвани, когато оптимизираният код работи с копия на техните полета, напр. в регистрите на процесора.