Java 垃圾收集 - 它有什么作用?

发布于 2024-12-09 12:28:51 字数 312 浏览 0 评论 0原文

我的 Java 老师(高中课程)正在谈论循环,她说如果你有一个 for 循环,例如:

for (int i = 0; i < max; i++) {
    //something
}

你不能在循环之外使用变量 i因为垃圾收集功能会删除它,因为它感觉到它是“不需要的”(我知道作用域,这是废话,因为所有语言都会发生同样的事情,而 C++ 甚至没有垃圾收集)。现在的问题是……垃圾收集实际上是做什么的? (我查了一下,它与我还不知道的堆有关,所以有人向我解释一下)

谢谢

My Java teacher (High school course) was talking about loops and she said that if you have a for loop like:

for (int i = 0; i < max; i++) {
    //something
}

you can't use the variable i outside of the loop because the garbage collection feature deletes it because it senses that it's "unneeded" (I know about scopes and that this is BS because the same thing happens in all languages and C++ doesn't even have garbage collection). Now the question is... What does Garbage Collection actually do? (I looked it up and it had something to do with heaps which I don't know about yet so someone explain this to me)

Thanks

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

只有一腔孤勇 2024-12-16 12:28:52

(我知道作用域,这是废话,因为所有语言都会发生同样的事情,而 C++ 甚至没有垃圾回收)。

正确的。由于作用域的原因,变量 i 不能在循环外部使用 - 它与 GC 没有任何关系(在潜在对象之外)可达能力)。

现在的问题是...垃圾收集实际上是做什么的? (我查了一下,它与我还不知道的堆有关,所以有人向我解释一下)

垃圾收集器负责回收不再存在的对象 强可达。尽管变量可以保持对象强可达性,但垃圾收集器与变量没有任何关系。 (此外,原始值,例如 int 不是对象,因此永远不会被 GC 处理;-)

我建议通读 《Java 虚拟机内部》第 9 章:
垃圾收集
垃圾收集的真相 因为我相信他们会提供足够的答案/见解和理由。 (垃圾收集 wikipedia 条目 也是一个很好的开始,很好地总结了 GC一般而言。)

来自“真相”:

当不再存在对其的强引用时,对象将进入不可访问状态[不可强可达]。 当一个对象无法访问时,它就是一个收集的候选对象请注意措辞:仅仅因为一个对象是收集的候选对象并不意味着它会立即被收集。 JVM 可以自由地延迟收集,直到对象立即需要消耗内存为止。

快乐编码。

(I know about scopes and that this is BS because the same thing happens in all languages and C++ doesn't even have garbage collection).

Correct. The variable i can't be used outside the loop because of scope -- it has nothing to do with the GC (outside of potential object reach-ability).

Now the question is... What does Garbage Collection actually do? (I looked it up and it had something to do with heaps which I don't know about yet so someone explain this to me)

The Garbage Collector is responsible for reclaiming objects which are no longer strongly-reachable. A garbage collector has nothing [directly] to do with variables, although a variable can keep an object strongly-reachable. (Also, primitive values, such as int are not objects are thus never dealt with by the GC ;-)

I would recommend reading through Chapter 9 of Inside the Java Virtual Machine:
Garbage Collection
and The Truth About Garbage Collection as I believe they will provide sufficient answers/insight and justification. (The Garbage Collection wikipedia entry is also a good start and nicely summarizes GC in general.)

From "The Truth":

An object enters an unreachable state when no more strong references to it exist [is not strongly-reachable]. When an object is unreachable, it is a candidate for collection. Note the wording: Just because an object is a candidate for collection doesn't mean it will be immediately collected. The JVM is free to delay collection until there is an immediate need for the memory being consumed by the object.

Happy coding.

很酷不放纵 2024-12-16 12:28:52

您老师的示例不是很好,因为 i 可能存储在堆栈中,因为它是原语。一个更好的例子是:

public String helloWorld() {
    StringBuilder builder = new StringBuilder();
    builder.append("Hello");
    builder.append(" ");
    builder.append("World!");
    return builder.toString();
}

在函数的第一行,我们分配一个新对象(new StringBuilder())。 在堆中分配一些内存,稍后需要释放这些内存。在 C++ 中,您可以在最后执行 delete builder 来处理该问题(或在堆栈上分配它 - 但您不能在 Java 中执行此操作,所以我认为这是一个合理的示例)。

垃圾收集是一种替代方法,在函数结束时 builder 不会发生任何事情。相反,一个称为垃圾收集器的进程会定期运行,检查哪些对象正在使用或未使用,并清除任何未使用的对象。在我给出的示例中,垃圾收集器将运行,注意到无法再访问 builder,然后将其删除。

Java 的默认垃圾收集器执行称为“标记和清除”的操作,它基本上遍历它可以访问的所有变量并标记它们(设置一些标志)。然后它会删除所有未标记的内容。

我认为在更低的层面上,它实际上所做的是将所有可访问的内容移动到新的内存位置,并删除旧内存位置中的所有内容(因为仍然无法访问任何内容)。

一种更简单的垃圾收集方法称为“引用计数”,其中动态分配的所有内容都有一个引用计数——告诉程序有多少变量指向该内存位置。如果引用计数达到 0,则表明没有人正在使用该内存,并且可以立即释放该内存。上次我检查时,标准 Python 解释器(CPython)使用了它。

引用计数的问题在于您可能会出现循环:

class Node {
    Node next;
}

public void breakReferenceCountingAlgorithm() {
    Node a = new Node();
    Node b = new Node();
    a.next = b;
    b.next = a;
}

在此函数末尾,a 和 b 都被引用一次(彼此),但它们不可访问。无论如何,Java 都会捕捉到这一点并对它们进行垃圾收集。 Python 不会。


另一方面,您不能在给出的循环之外使用 i 的原因是范围界定,而不是垃圾收集。在函数内部,i 的内存可能仍然可用,只是编译器不允许您访问它。主要是这样你可以这样做:

for(int i = 0; i < 100; i++) {
    System.out.println("stuff");
}

// This i is a different variable
for(int i = 0; i < 100; i++) {
    System.out.println("more stuff");
}

Your teacher's example isn't very good, because i is probably being stored on the stack since it's a primitive. A better example would be:

public String helloWorld() {
    StringBuilder builder = new StringBuilder();
    builder.append("Hello");
    builder.append(" ");
    builder.append("World!");
    return builder.toString();
}

In the first line of the function we allocate a new object (new StringBuilder()). This allocates some memory in the heap, which later needs to be freed. In C++, you'd do delete builder at the end to handle that (or allocate it on the stack -- but you can't do that in Java so I think this is a reasonable example).

Garbage collection is an alternate method, where nothing happens to builder at the end of the function. Instead, periodically, a process called the garbage collector runs, and checks what objects are or aren't in use, and gets rid of anything that's not. In the example I gave, the garbage collector will run, notice that there's no way to access builder anymore, and delete it.

Java's default garbage collector does something called "mark and sweep", where it basically walks through all of the variables it can access and marks them (some flag is set). Then it deletes anything that's not marked.

I think at an even lower level, what it actually does is move everything that's accessible to a new memory location, and delete anything in the old memory location (because anything still there wasn't accessible).

An easier garbage collection method is called "reference counting", where everything that's dynamically allocated has a reference count -- telling the program how many variables are pointed at that memory location. If the reference count ever hits 0, no one is using that memory and it can be immediately freed. Last time I checked, the standard Python interpreter (CPython) uses this.

The problem with reference counting is that you can get loops:

class Node {
    Node next;
}

public void breakReferenceCountingAlgorithm() {
    Node a = new Node();
    Node b = new Node();
    a.next = b;
    b.next = a;
}

At the end of this function, a and b are both referenced once (by each other), but they're not accessible. Java will catch this and garbage collect them anyway. Python would not.


On another note, the reason you can't use i outside of that loop you gave is scoping, not garbage collection. Inside of the function, i's memory is probably still available, the compiler just won't let you access it. Mainly so you can do this:

for(int i = 0; i < 100; i++) {
    System.out.println("stuff");
}

// This i is a different variable
for(int i = 0; i < 100; i++) {
    System.out.println("more stuff");
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文