Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

Re: Caching wrapped objects in custom WrapFactory

9 views
Skip to first unread message

Daryl Stultz

unread,
Jan 4, 2010, 8:19:32 AM1/4/10
to Rhino JS User List
On Mon, Jan 4, 2010 at 7:13 AM, Attila Szegedi <szeg...@gmail.com> wrote:

> In my other project, FreeMarker, I implemented optional caching of
> wrappers. By default, it is turned off. I heard of some people that
> turned it on, but in most use cases, there are no noticeable
> performance differences.
>
> While you should test every performance hypothesis with a profiler,
> the common presumption these days is that these objects are
> sufficiently light to create that their repeated creation and
> collection (they'll likely not even survive the first eden space
> collection) is too insignificant burden to the GC to justify keeping
> them around.
>
> They're simply too light to cache.
>
>
My concern was not memory use but the expense of method lookups via
reflection. I imagine Rhino does a lot of caching of the lookups, then?

--
Daryl Stultz
_____________________________________
6 Degrees Software and Consulting, Inc.
http://www.6degrees.com
mailto:da...@6degrees.com

Attila Szegedi

unread,
Jan 4, 2010, 8:54:17 AM1/4/10
to Daryl Stultz, Rhino JS User List
On 2010.01.04., at 14:19, Daryl Stultz wrote:

> On Mon, Jan 4, 2010 at 7:13 AM, Attila Szegedi <szeg...@gmail.com> wrote:
>
>> In my other project, FreeMarker, I implemented optional caching of
>> wrappers. By default, it is turned off. I heard of some people that
>> turned it on, but in most use cases, there are no noticeable
>> performance differences.
>>
>> While you should test every performance hypothesis with a profiler,
>> the common presumption these days is that these objects are
>> sufficiently light to create that their repeated creation and
>> collection (they'll likely not even survive the first eden space
>> collection) is too insignificant burden to the GC to justify keeping
>> them around.
>>
>> They're simply too light to cache.
>>
>>
> My concern was not memory use but the expense of method lookups via
> reflection. I imagine Rhino does a lot of caching of the lookups, then?

Caching is performed once per class and top-level scope, and results stored in an instance of ClassCache that is associated with the topmost scope.

As an implication, if you use a new top-level scope for every script execution (which you normally do), you'll end up with the cache being recreated on a per-scope basis. If you use a shared-prototype top-level scope, you'll avoid this (but then you have to use shared-prototype top-level scope pattern, which I generally dislike).

The problem is that every Java method needs to be exposed as a JavaScript Function, and as such it needs to have its internal "prototype" property set to the Function() constructor, which is found in a properly initialized (initStandardObjects) top-level scope. Hence, the created and cached reflective data depends upon a Function constructor instance in a standardly-initialized scope.

Your choices are basically:
a) use one non-shared top-level scope per script execution, suffer from repeated reflection lookups between script executions

b) use one shared scope per script execution as a prototype of non-shared per-execution scopes. Drawbacks: need to use dynamic scoping (nonstandard JS), and scripts have shared state *that is mutable*.

c) have one ClassCache and assign it to every scope you create - see ClassCache#associate() and ClassCache#get- you'll have to do it on the scope *before* calling initStandardObjects() as it'll install an empty new ClassCache into it. Drawback: Java methods and constructors mirrored into your scope will use Function prototype from another scope (the one ClassCache was first associated with) - I'm not sure if this can cause any problems (aside from never releasing that first scope from memory, which is a minor, fixed size memory usage increase), it might actually work in your scenario.

Attila.

--
home: http://www.szegedi.org
twitter: http://twitter.com/szegedi
weblog: http://constc.blogspot.com

Daryl Stultz

unread,
Jan 4, 2010, 9:03:41 AM1/4/10
to Rhino JS User List
>
> On Mon, Jan 4, 2010 at 8:54 AM, Attila Szegedi <szeg...@gmail.com> wrote:
>
>
>> Your choices are basically:
>> a) use one non-shared top-level scope per script execution, suffer from
>> repeated reflection lookups between script executions
>>
>> b) use one shared scope per script execution as a prototype of non-shared
>> per-execution scopes. Drawbacks: need to use dynamic scoping (nonstandard
>> JS), and scripts have shared state *that is mutable*.
>>
>> c) have one ClassCache and assign it to every scope you create - see
>> ClassCache#associate() and ClassCache#get- you'll have to do it on the scope
>> *before* calling initStandardObjects() as it'll install an empty new
>> ClassCache into it. Drawback: Java methods and constructors mirrored into
>> your scope will use Function prototype from another scope (the one
>> ClassCache was first associated with) - I'm not sure if this can cause any
>> problems (aside from never releasing that first scope from memory, which is
>> a minor, fixed size memory usage increase), it might actually work in your
>> scenario.
>>
>
Thanks, Attila, this is good information I'll file away until I get the time
to address performance.

Johan Compagner

unread,
Jan 4, 2010, 10:38:36 AM1/4/10
to Rhino JS User List
the thing is that JavaMembers could cache the reflect stuff inside itself in
a WeakHashmap
So that it doesnt have to do the reflection over and over again.
Then it should not matter if there is a ClassCache or not.


On Mon, Jan 4, 2010 at 14:54, Attila Szegedi <szeg...@gmail.com> wrote:

> On 2010.01.04., at 14:19, Daryl Stultz wrote:
>

> Your choices are basically:
> a) use one non-shared top-level scope per script execution, suffer from
> repeated reflection lookups between script executions
>
> b) use one shared scope per script execution as a prototype of non-shared
> per-execution scopes. Drawbacks: need to use dynamic scoping (nonstandard
> JS), and scripts have shared state *that is mutable*.
>
> c) have one ClassCache and assign it to every scope you create - see
> ClassCache#associate() and ClassCache#get- you'll have to do it on the scope
> *before* calling initStandardObjects() as it'll install an empty new
> ClassCache into it. Drawback: Java methods and constructors mirrored into
> your scope will use Function prototype from another scope (the one
> ClassCache was first associated with) - I'm not sure if this can cause any
> problems (aside from never releasing that first scope from memory, which is
> a minor, fixed size memory usage increase), it might actually work in your
> scenario.
>

> > --
> > Daryl Stultz
> > _____________________________________
> > 6 Degrees Software and Consulting, Inc.
> > http://www.6degrees.com
> > mailto:da...@6degrees.com
>
>

> _______________________________________________
> dev-tech-js-engine-rhino mailing list
> dev-tech-js-...@lists.mozilla.org
> https://lists.mozilla.org/listinfo/dev-tech-js-engine-rhino
>

Daryl Stultz

unread,
Jan 4, 2010, 11:02:30 AM1/4/10
to Rhino JS User List
On Mon, Jan 4, 2010 at 10:38 AM, Johan Compagner <jcomp...@gmail.com>wrote:

> the thing is that JavaMembers could cache


Could it, should it, does it? Is this a feature/implementation request?

Daryl Stultz

unread,
Jan 4, 2010, 11:07:03 AM1/4/10
to Rhino JS User List
On Mon, Jan 4, 2010 at 8:54 AM, Attila Szegedi <szeg...@gmail.com> wrote:

> As an implication, if you use a new top-level scope for every script
> execution (which you normally do), you'll end up with the cache being
> recreated on a per-scope basis. If you use a shared-prototype top-level
> scope, you'll avoid this (but then you have to use shared-prototype
> top-level scope pattern, which I generally dislike).
>

This page:
https://developer.mozilla.org/En/Rhino_documentation/Scopes_and_Contexts
under "Sharing Scopes" states "for our purposes it gives us an easy way to
share a set of read-only variables across multiple scopes". Is this section
describing the "shared-prototype top-level scope pattern" you dislike? Your
drawbacks state it is mutable but the line I cited says "read-only".

Perhaps you can describe how it is "non-standard". How would someone writing
the JavaScript get tripped up by this?

Thanks.

Johan Compagner

unread,
Jan 4, 2010, 11:07:19 AM1/4/10
to Rhino JS User List
it could do it (doesnt do it)
At least not the code i look at currently (still using 1.6R7)

But then if we cache that reflection in a static weakhashmap<Class,xxxx>
we could just not get it from the top level scope ClassCache but directly
internally so what JavaMembers do have a private static
WeakHashmap<Class,JavaMembers> member...

But i guess the ClassCache attached to a scope is in purpose?

johan


On Mon, Jan 4, 2010 at 17:02, Daryl Stultz <da...@6degrees.com> wrote:

> On Mon, Jan 4, 2010 at 10:38 AM, Johan Compagner <jcomp...@gmail.com
> >wrote:
>
> > the thing is that JavaMembers could cache
>
>
> Could it, should it, does it? Is this a feature/implementation request?
>

> --
> Daryl Stultz
> _____________________________________
> 6 Degrees Software and Consulting, Inc.
> http://www.6degrees.com
> mailto:da...@6degrees.com

Attila Szegedi

unread,
Jan 4, 2010, 12:47:35 PM1/4/10
to Johan Compagner, Rhino JS User List
There could be some further separation of scope-independent data (basically, the MemberBox objects obtained through reflection, i.e. actual java.lang.reflect.* objects) and scope-dependent data (JavaMembers instances).

Yes, ClassCache attached to a scope is on purpose since it has the Class->JavaMembers map. JavaMembers has a bunch of NativeJavaMethod instances, which are JavaScript functions (they extend BaseFunction), and as such need to answer true to "instanceof Function", hence they are scope specific, as they bind their prototype to the object returned by the expression "Function.prototype" in that scope.

So, yes, this could be refined by separating the caching in two - MemberBox[] objects are scope-independent, and more expensive to create, while JavaMembers are scope-specific and less expensive to create.

Attila.

On 2010.01.04., at 17:07, Johan Compagner wrote:

> it could do it (doesnt do it)
> At least not the code i look at currently (still using 1.6R7)
>
> But then if we cache that reflection in a static weakhashmap<Class,xxxx>
> we could just not get it from the top level scope ClassCache but directly
> internally so what JavaMembers do have a private static
> WeakHashmap<Class,JavaMembers> member...
>
> But i guess the ClassCache attached to a scope is in purpose?
>
> johan
>
>
> On Mon, Jan 4, 2010 at 17:02, Daryl Stultz <da...@6degrees.com> wrote:
>
>> On Mon, Jan 4, 2010 at 10:38 AM, Johan Compagner <jcomp...@gmail.com
>>> wrote:
>>
>>> the thing is that JavaMembers could cache
>>
>>
>> Could it, should it, does it? Is this a feature/implementation request?
>>
>> --
>> Daryl Stultz

Attila.

Attila Szegedi

unread,
Jan 4, 2010, 1:01:14 PM1/4/10
to Daryl Stultz, Rhino JS User List
On 2010.01.04., at 17:07, Daryl Stultz wrote:

> On Mon, Jan 4, 2010 at 8:54 AM, Attila Szegedi <szeg...@gmail.com> wrote:
>
>> As an implication, if you use a new top-level scope for every script
>> execution (which you normally do), you'll end up with the cache being
>> recreated on a per-scope basis. If you use a shared-prototype top-level
>> scope, you'll avoid this (but then you have to use shared-prototype
>> top-level scope pattern, which I generally dislike).
>>
>
> This page:
> https://developer.mozilla.org/En/Rhino_documentation/Scopes_and_Contexts
> under "Sharing Scopes" states "for our purposes it gives us an easy way to
> share a set of read-only variables across multiple scopes". Is this section
> describing the "shared-prototype top-level scope pattern" you dislike? Your
> drawbacks state it is mutable but the line I cited says "read-only".
>
> Perhaps you can describe how it is "non-standard". How would someone writing
> the JavaScript get tripped up by this?

Normally, it doesn't. I myself have a system that uses this approach that churns over a million script executions per day and encounters no problems. The problem is, you'll hardly be able to completely lock down the shared scope - while you can not easily put anything into it, you can, say, have one script extend or replace functions of standard objects (i.e. Array.push = function() { ... } - you get the idea) and have it be observed by all others. You can, of course, go through the full reachable JS object graph in the shared scope by recursively calling getIds() and then call ScriptableObject.seal() on every one of them.

Then in turn, you'll have a problem if you (or users of your system) will actually expect to be able to modify built-in objects (i.e. by adopting a JS library that attempts to do it) and failing. Now, of course, you could "install" that library into the shared scope by executing its function-defining scripts in that scope, and that'll solve the problem. However, you then might run into the problem of that library using "x" instead of "this.x" to refer to some globally defined "x" - it'll fail if "x" is actually defined in the individual non-shared scopes. Witness:

shared scope has these functions defined:

function getGlobalX() {
return x;
}

function getThisX() {
return this.x;
}

a script then runs against individual scope that has the shared scope as the prototype:

var x = 1;
print(getThisX());
print(getGlobalX());

getThisX() will succeed, but getGlobalX() will fail. If you can, fix the functions to use explicit "this." (or not rely on global identifiers - even better). If you can not, then you need to turn on Rhino's "dynamic scopes" feature, which introduces non-standard scoping resolution rules (as JS language specification prescribes use of lexical scoping), and can then create subtle bugs with libraries that use lexical scopes to implement private members.

So, yes, you can do it, and I'm not speaking against it, just making sure you know the potential pitfalls - if they don't apply to your system, all the better.

Attila.

>
> Thanks.

Daryl Stultz

unread,
Jan 4, 2010, 1:16:12 PM1/4/10
to Rhino JS User List
On Mon, Jan 4, 2010 at 1:01 PM, Attila Szegedi <szeg...@gmail.com> wrote:

> On 2010.01.04., at 17:07, Daryl Stultz wrote:

> You can, of course... Then in turn, ...of course, you could ... then might
>

Wow, this is a bit deeper than I thought. Thanks for the details. What's
your opinion of this:
http://mxr.mozilla.org/mozilla/source/js/rhino/examples/DynamicScopes.java

Is this an implementation of what you consider problematic/challenging or is
this something different?

0 new messages