在Java中,哪些对象放在堆栈上,哪些放在堆上?

Har*_*han 4 java heap stack

对于Java函数中的语句:

Xxx xxx = new Xxx() {
    public Abc abc(final Writer out) {
        return new SomeFunction(out) {
            boolean isDone = false;
            public void start(final String name) {
                /* blah blah blah */
            }
        };
    }
};
Run Code Online (Sandbox Code Playgroud)

哪个变量(包括函数)放在堆上,哪些变量放在堆栈上?

我问这个的原因是JVM中的分段错误:

kernel: java[14209]: segfault at 00002aab04685ff8 rip 00002aaab308e4d0 rsp 00002aab04685ff0 error 6
Run Code Online (Sandbox Code Playgroud)

00002aab04685ff8并且00002aab04685ff0附近,似乎堆栈增长太快.我尝试研究这部分代码,并怀疑它是多次调用此函数时是否是问题的原因.如果堆被堆上的某些变量引用,堆栈是否可能被清除?

rol*_*lve 5

关于特定对象是否在堆上的问题有点涉及.

通常,在Java中,所有对象都在堆上分配,因为方法可以在某处返回或存储指向对象的指针.如果对象已经放在堆栈上,那么下次堆栈框架放在那里时就会覆盖它.

但是,HotSpot JIT编译器会执行一个名为Escape Analysis的操作.此分析通过查看其实现来查明对象是否"逃避"该方法的范围.如果对象没有转义,编译器可以安全地在堆栈上分配它.

维基百科有关于Java中的Escape Analysis的更多信息,还有关于多线程和锁定的信息.


关于堆栈溢出:在方法完成后,总是删除调用堆栈上的堆栈帧.实际上,甚至没有必要明确删除它.下一帧将覆盖以前的帧.

此外,虽然在其他语言(如C)中,可能通过在堆栈上放置非常大的对象来导致堆栈溢出,但我认为这不会发生在Java中.我希望Sun(Oracle)的工程师足够聪明,不要让VM在堆栈中存储大量对象.

因此,堆栈溢出的唯一可能性是具有太多嵌套方法调用.由于堆栈空间非常大,足以处理任何"普通"方法调用嵌套,因此堆栈溢出通常意味着代码中的无限(或非常大)递归.

  • 例如,尝试`for(Integer i = 0; i <Integer.MAX_VALUE; i ++);`with`-verbosegc`;) (3认同)
  • 在堆栈上有相当多的对象有很多原因:首先,堆栈在缓存中的概率要高得多,为您提供局部变量的缓存局部性.其次,堆上的对象越少,垃圾收集器就越不繁忙.第三,保证堆栈上的对象只能由一个线程访问,因此不需要锁定它们. (2认同)
  • @rolve优秀摘要.我想补充说,编译是在后台线程中完成的,所以它的影响相对较小.不幸的是,逃逸分析很复杂,很少(根据我的经验)导致在HotSpot编译器中消除甚至是微不足道的对象.我已经看到删除了varargs数组,但没有其他的.JRockit JVM应该会更好,但我自己也没见过. (2认同)