堆、非堆和栈——垃圾回收的复杂性

4

我正在研究Java(JDK 6 HotSpot JVM)的垃圾回收。我有几个问题,希望社区能够帮助我解决。

我的理解:

1)堆被划分为

a)年轻代-伊甸园和幸存者:新对象和数组会创建到年轻代中。小的垃圾回收将在年轻代中进行。那些还活着的对象将被从伊甸园空间移动到幸存者空间。

b)老年代/终身代:大规模的垃圾回收将从年轻代将这些还活着的对象移到老年代。

2)非堆被划分为

    a)Code Cache
    b)Perm generation.

我想知道的是:


1)what if survivor gets full..how will minor garbage collection work.

2)When and how is the perm generation garbage collected.
3)Also what happens to the stack..where is it stored or residing?How is its size controlled?

2
如果幸存者区满了,那么对象将被移动到老年代。 - BevynQ
你有多个问题。 - Jabir
3个回答

1
  1. 当Survivor空间满时,对象被移动到旧生代。尽管在大多数情况下,当一个对象从Survivor空间移动到旧生代时,并不是因为Survivor空间已满,而是因为该对象已经经历了一定数量的Minor GC,通常是10-15次。
  2. 非常罕见。它主要是Java类的二进制代码,因此只有在内存中卸载了一堆类时才能释放空间。大多数程序在整个程序的生命周期中使用相同的类集,因此收集永久代通常是浪费时间的。基本上,只有当Java即将耗尽内存时,它才会在此处做一次垃圾回收。
  3. 栈是堆之外的东西,其大小由以下事实控制:只有在保证对象具有有限生存期的情况下,才将对象存储在栈上。这些对象大多是局部变量。假设您有一个本地的StringBuilder变量,您用它来构建方法的返回值。您从未将其传递到自己的方法之外,并调用stringBuilder().toString()在方法结束时创建一个新对象。由于Java可以确定StringBuilder对象不会超出方法运行的寿命,因此它可以将其放在堆栈上,并在方法返回时立即将其取消分配,而不是将其传递给垃圾回收器。

谢谢回复...对于第二部分,只有在应用程序未部署时才会卸载类?我是正确的吗?我正在检查我的一个应用程序,在其中由jconsole捕获的永久代生成图形是一种锯齿状的图形...你如何解释这个问题...这可能是因为字符串内部也驻留在永久代中吗? - Rips
@Rips 是的,在卸载应用程序后,永久代中的空间可以被释放,但具体情况严重依赖于您在旧一代中使用的垃圾收集器。然而,随着Java 8的到来,永久代已经消失了 - andrewdotn
如果我们谈论HotSpot JVM,那么就不可能在堆栈上分配StringBuilder对象。JVM可以内联所有StringBuilder方法,并将其字段转换为方法范围内的本地变量。这样,确实,StringBuilder对象将不会在堆中分配。但是,用于组成字符串的临时char[]仍将在堆上分配。 - Alexey Ragozin

0

栈是内存的一部分。本地自动变量在此栈上创建,方法参数也会传递到此栈上。当进程启动时,它会获得一个默认的栈大小,对于每个进程来说都是固定的。在今天的操作系统中,通常默认的栈大小为1 Mb,这已经足够满足大多数进程的需求。在异常情况下,栈限制超出了范围。这就是所谓的栈溢出。


0

栈的大小是在创建点时被固定控制的。如果您尝试使用比堆栈上可用空间更多的空间,将会出现“堆栈溢出”异常。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接