Pet*_*aný 40 java variables null garbage-collection local
myLocalVar = null;在离开方法之前,我被"强制"在finally子句中添加语句.原因是帮助GC.有人告诉我,下次服务器崩溃时我会收到短信,所以我最好这样做:-).
我认为这是毫无意义的,因为myLocalVar的范围是方法,并且一旦方法退出就会"丢失".额外的归零只会污染代码,否则无害.
我的问题是,这个关于帮助GC的神话来自哪里?(我被称为"Java记忆书")你知道"权威"的任何文章更深入地解释它吗?有可能这不是一个神话,但真的有所帮助吗?如果是这样,怎么样?可能会使局部变量归零会造成任何伤害?
为了澄清,方法看起来像这样:
void method() {
MyClass myLocalVar = null;
try {
myLocalVar = get reference to object;
... do more here ...
} finally {
if (myLocalVar != null) {
myLocalVar.close(); // it is resource which we should close
}
myLocalVar = null; // THIS IS THE LINE I AM TALKING ABOUT
}
}
Run Code Online (Sandbox Code Playgroud)
Uri*_*Uri 18
Java GC应该是"声音"但不一定立即"完整".换句话说,它被设计成永远不会消除至少一条路径仍可访问的对象(从而导致悬空引用).它不一定立即完成,因为它可能需要一些时间才能删除所有可以删除的内容.
我认为大多数GC神话来自对这个概念的误解.许多人保留了太多的实例变量,这会导致问题,但这当然不是问题.
其他人将局部变量放在一个实例变量中(例如,通过将其传递给函数),然后认为使局部变量无效以某种方式消除变量,这当然是不真实的.
最后,有些人过分关注GC并认为它会对它们进行功能关闭(例如,当删除变量时关闭连接),当然不是这种情况.我认为这一行的来源是"我真的真的用它,但我不知道如何确保".
所以,是的,你是正确的,这是不必要的.
据我所知,null在它离开作用域之前立即变量对垃圾收集器没有任何影响.
当然,有些情况确实有所帮助.例如,何时var不是局部变量而是成员或静态成员.然后销毁引用可能会使对象无法访问,因此有资格进行收集.
如果一个函数分配了很多临时内存来初始化一些数据以便进一步处理,并且在开始处理之前可以丢弃对临时内存的所有引用,那么它甚至可能对局部变量有帮助的情况:
SomeResult SomeFunction(SomeClass param) {
TempData big = new TempData(param);
IntermediateResult intermediate = big.GetIntermediateResult();
big = null; // allow GC to reclaim the memory before returning from the function
intermediate.FurtherProcessing();
return intermediate.EvenMoreProcessing();
}
Run Code Online (Sandbox Code Playgroud)
排除局部变量确实可以在某些情况下提供帮助。这不适用于原始问题中的情况,但是仍然具有教育意义。让我们考虑一下该程序:
public class Main {
public static void main(String[] args) {
{
Main local = new Main();
// inner = null;
}
while (true) {
// long running method
}
}
}
Run Code Online (Sandbox Code Playgroud)
如果inner = null;将其注释掉,则local在while循环期间无法对变量中的对象进行垃圾回收。原因是Java虚拟机不了解这样的范围。它所具有的只是:
D:\workspaces\workspace-3.4\test\src>javap -verbose -c Main
public class Main extends java.lang.Object
minor version: 0
major version: 50
Constant pool:
const #1 = Method #4.#11; // java/lang/Object."<init>":()V
const #2 = class #12; // Main
const #3 = Method #2.#11; // Main."<init>":()V
const #4 = class #13; // java/lang/Object
const #5 = Asciz <init>;
const #6 = Asciz ()V;
const #7 = Asciz Code;
const #8 = Asciz main;
const #9 = Asciz ([Ljava/lang/String;)V;
const #10 = Asciz StackMapTable;
const #11 = NameAndType #5:#6;// "<init>":()V
const #12 = Asciz Main;
const #13 = Asciz java/lang/Object;
{
public Main();
Code:
Stack=1, Locals=1, Args_size=1
0: aload_0
1: invokespecial #1; //Method java/lang/Object."<init>":()V
4: return
public static void main(java.lang.String[]);
Code:
Stack=2, Locals=2, Args_size=1
0: new #2; //class Main
3: dup
4: invokespecial #3; //Method "<init>":()V
7: astore_1
8: goto 8
StackMapTable: number_of_entries = 1
frame_type = 8 /* same */
}
Run Code Online (Sandbox Code Playgroud)
没有有关局部变量范围的信息。因此,从JVM的角度来看,上述程序等效于:
public class Main
{
public Main() { }
public static void main(String args[])
{
Main main1 = new Main();
do
;
while(true);
}
}
Run Code Online (Sandbox Code Playgroud)
(由JAD反编译器生成)
结论:在这样的非常特殊的情况下,使局部变量为空有一些理由。但是,如果方法即将完成(例如在我最初的问题中),则无济于事。
这是受Zdenek Tronicek在java-cz邮件列表上的评论启发的(捷克语,很抱歉)