This is the mail archive of the kawa@sourceware.org mailing list for the Kawa project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

Code generation problem?


Hi,

[this is a rather long and technical post]

While trying to remove some memory leaks in my code occurring on WebSphere
5.1 using IBM?s JVM, I observed something strange. Some closed procedures
are stored in static fields of some Kawa generated classes each time an
activation frame is created. 

Consider the following code:

  (module-name <test>)
  (module-static 'init-run)

  (require 'srfi-1)

  (define (foo lst x)
    (apply (lambda (a b) (+ a b))
           (filter (cut eq? <> x) lst)))


The first argument to 'apply' in function 'foo' is a closed procedure. It
does not depend on the lexical environment, only on the global environment.
However, the first argument to 'filter' is a procedure that requires a full
closure (it references the 'x' variable).

For this code, Kawa creates a class 'test$frame' that represents the lexical
environment shared by both procedures. Everything is fine so far. When this
frame is instantiated, two instances of ModuleMethod are created, one for
each of the two procedures. Again, no problem here. 

Now look at the generated code:

  public class test$frame extends gnu.expr.ModuleBody{
  java.lang.Object x;

  static gnu.expr.ModuleMethod lambda$Fn1;

  final gnu.expr.ModuleMethod lambda$Fn2;

  static java.lang.Object lambda1(java.lang.Object,java.lang.Object);
    Code:
     0:   getstatic       #6; //Field
gnu/kawa/functions/AddOp.$Pl:Lgnu/kawa/functions/AddOp;
     3:   aload_0
     4:   aload_1
     5:   invokevirtual   #12; //Method
gnu/mapping/Procedure.apply2:(Ljava/lang/Object;Ljava/lang/Object;)Ljav
a/lang/Object;
     8:   areturn

  boolean lambda2(java.lang.Object);
    Code:
     0:   aload_1
     1:   aload_0
     2:   getfield        #18; //Field x:Ljava/lang/Object;
     5:   if_acmpne       12
     8:   iconst_1
     9:   goto    13
     12:  iconst_0
     13:  ireturn

The field 'lambda$Fn1' is static! And since it holds a ModuleMethod, which
keeps a reference to the frame itself, non-static objects like 'x' will be
reachable from a static field. If 'x' is a rather large object, it will
never be reclaimed by the GC (unless I set! it to #!null explicitely in my
purely functional code...) I think this is a problem. Moreover, the
initialization of the field is done each time a frame is created. If the
goal was to optimize something, it would have made more sense, IMO, to
initialize it only once. 

This clearly breaks the rule of least surprise, IMO, as it has some
unexpected side-effects. I prefer to program in a purely functional style
wherever possible and I am careful about the global environment. But this
compilation strategy may force me to adopt a very different (read: ugly ;-)
coding style... 

What do you think? What is the rationale about that optimization?

[I?m using an old version of Kawa (pre 1.8), so this problem may have been
fixed by now. I just wanted to know your opinion on all this.]

Thanks,

Dominique Boucher
Lead Software Developer
Nü Echo Inc.
http://www.nuecho.com




Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]