[讨论] 关于OpenJDK1.7的javac源码中JavaCompiler类的一点疑问

budairenqin 2012-01-06
com.sun.tools.javac.main.JavaCompiler的
public void compile(List<JavaFileObject> sourceFileObjects,
                        List<String> classnames,
                        Iterable<? extends Processor> processors)
方法中有如下代码:
            /*
             * parseFiles:词法分析、语法分析}
             * enterTrees:{输入到符号表}
             * processAnnotations:{执行注解处理}
             * delegateCompiler.compile2:{分析及字节码生成} 详细参照compile2方法内注释
             */
            // These method calls must be chained to avoid memory leaks       
            //这些方法调用必须连接起来以避免内存泄漏
            delegateCompiler =
                processAnnotations(
                    enterTrees(stopIfError(CompileState.PARSE, parseFiles(sourceFileObjects))),
                    classnames);
            delegateCompiler.compile2();

代码中有英文注释:
These method calls must be chained to avoid memory leaks
说这些方法调用连接起来能避免内存泄露
我猜是不是这样连接着的方法调用会减少一点对应java栈的大小?(比如一个方法的局部变量表和另一个方法的操作数栈共享一块内存什么的)
没想明白,有没有高手给点指点?

2012.1.9补充:以下分析不知道在不在理,高手给看看呗
我们知道:
1.“参数”是放在方法栈帧的局部变量表里的
2.方法的返回值在方法返回之前要压在栈顶留给调用它的函数使用的
3.jvm会不会考虑做这样的优化:一个“方法”的返回值和“调用它的方法”的局部变量表共用着相同一块内存来存放List<JCCompilationUnit>类型的数据(这个数据分别是“方法”的返回值和“调用它的方法”的参数)而且List<JCCompilationUnit>是存放至所有待编译源文件的语法树的列表,这个数据量真的很大


FX啊,IcyFenix啊 你么快来吧
budairenqin 2012-01-09
高手们快现身吧,从发问到现在三天我一会就过来看一下,就是没人回
RednaxelaFX 2012-01-09
budairenqin 写道
代码中有英文注释:
These method calls must be chained to avoid memory leaks
说这些方法调用连接起来能避免内存泄露
我猜是不是这样连接着的方法调用会减少一点对应java栈的大小?(比如一个方法的局部变量表和另一个方法的操作数栈共享一块内存什么的)
没想明白,有没有高手给点指点?

其实没啥特别的啦。看Mercurial的版本记录可以知道那行注释在OpenJDK代码库最初创建时就已经是那个样子的了。这么年代的代码想找最初的出处都找不出来了。

硬要猜的话,它原本的思路应该是这样的:那些嵌套的方法,每个内层调用的返回值都是外一层调用的参数;那么不赋值给显式声明的局部变量可以减少这些返回了然后又马上被当作参数传下去的引用的作用域。请参考这两帖看作用域与GC的关系:
http://rednaxelafx.iteye.com/blog/1044951
http://rednaxelafx.iteye.com/blog/1042471
减小作用域是跟“显式把返回值赋值给一个有名字的局部变量”相比而言。

那行注释在Sun JDK上只对解释器中跑这段代码有意义,如果这段代码被编译了就没啥意义了。不过因为这段代码实际上在相当靠近“顶层”的地方,执行次数确实会比较少因而很可能一直在解释器里跑,所以这段注释仍然有意义。

budairenqin 写道
2012.1.9补充:以下分析不知道在不在理,高手给看看呗
我们知道:
1.“参数”是放在方法栈帧的局部变量表里的
2.方法的返回值在方法返回之前要压在栈顶留给调用它的函数使用的
3.jvm会不会考虑做这样的优化:一个“方法”的返回值和“调用它的方法”的局部变量表共用着相同一块内存来存放List<JCCompilationUnit>类型的数据(这个数据分别是“方法”的返回值和“调用它的方法”的参数)而且List<JCCompilationUnit>是存放至所有待编译源文件的语法树的列表,这个数据量真的很大

3、可以是可以,但你得知道HotSpot VM在x86上并不是这么做的,其它JVM实现可以有别的选择。在x86上HotSpot VM在callee一侧用EAX寄存器来返回值(x64上用RAX,同理),而在caller一侧如果是在解释器里跑那么返回值会放在栈顶,如果caller是编译后的代码那返回值就在EAX(或RAX)里。Java的方法最多只能返回一个值;如果返回值是引用类型的,那它就返回一个引用,无论那个引用指向的是一个很大的list还是一个很小的list还是null都没啥区别。
budairenqin 2012-01-09
RednaxelaFX 写道
其实没啥特别的啦。看Mercurial的版本记录可以知道那行注释在OpenJDK代码库最初创建时就已经是那个样子的了。这么年代的代码想找最初的出处都找不出来了。

硬要猜的话,...

谢谢FX的指点,想明白了许多,我想错了,即使一个方法局部变量表和另一个函数的操作数栈共用,里面也只是存放List的引用,并不会起到太大的作用
至于你说的JIT编译部分,前几天才在群组共享里下载了hsdis,还没开始研究,慢慢消化吧
sidneyHere 2012-02-14
你看的好细啊
我们公司的代码里面也有很多这种chained调用,不知道是不是为了防止memory leak
Global site tag (gtag.js) - Google Analytics