@Memoize

856 views
Skip to first unread message

ka

unread,
May 13, 2012, 8:27:03 PM5/13/12
to Project Lombok
Hi,

We use lombok for project at work and it has worked quite well. It
would not be an exaggeration to say it has made Java programming less
sucky for us! So thanks for this very nice lib.

How about an @Memoize annotation. I'm aware of all the pitfalls and
misuse it can lead to. But still, if it is not too intelligent, it
will be quite helpful. Especially in my project at work I can see
extensive use for it.

@Memoize:
* Over non void non abstract methods
* Based on Equals and hashcode of method parameters
* Thread safe
* At any time only one thread should do computation for one set of
params
* Can be a memory leak if misused
* Absolutely NOT a substitute for proper caching

Here's prototype delomboked output: https://gist.github.com/2690759.

I've cloned the git repo and stubbed out the prototype for Javac. It
was quite a struggle understanding the sun compiler api without any
documentation. I hope Ecj will be simpler. Following test
https://gist.github.com/2690898 seems to be working right now.

If you guys think @Memoize has a place in lombok, I will be happy to
contribute. In any case, will appreciate comments on the delomboked
output.

Thanks

Roel Spilker

unread,
May 14, 2012, 4:12:09 AM5/14/12
to project...@googlegroups.com
We've been thinking about this feature for some time now! Tonight I'll look at your proposal and give some feedback.

Tumi

unread,
May 14, 2012, 4:51:24 AM5/14/12
to Project Lombok
In order to avoid memory leaks / empty the "cache" when needed a GC,
SoftReference shoud be used, so instead of:
private final java.util.concurrent.ConcurrentHashMap
__lombok_test_c_0 = new java.util.concurrent.ConcurrentHashMap();

It could be be:
private final SoftReference<ConcurrentHashMap> __lombok_test_c_0 =
new new SoftReference<ConcurrentHashMap>(new ConcurrentHashMap());

P.S: "SoftReference<Map<K,V>>" is preferred over "Map<K,
SoftReference<V>>" in terms of GC time/work, see
http://www.ibm.com/developerworks/java/library/j-jtp01246/index.html

Regards



On May 14, 10:12 am, Roel Spilker <r.spil...@gmail.com> wrote:
> We've been thinking about this feature for some time now! Tonight I'll look
> at your proposal and give some feedback.
>
>
>
>
>
>
>
> On Monday, May 14, 2012 2:27:03 AM UTC+2, ka wrote:
>
> > Hi,
>
> > We use lombok for project at work and it has worked quite well. It
> > would not be an exaggeration to say it has made Java programming less
> > sucky for us! So thanks for this very nice lib.
>
> > How about an @Memoize annotation. I'm aware of all the pitfalls and
> > misuse it can lead to. But still, if it is not too intelligent, it
> > will be quite helpful.  Especially in my project at work I can see
> > extensive use for it.
>
> > @Memoize:
> > * Over non void non abstract methods
> > * Based on Equals and hashcode of method parameters
> > * Thread safe
> > * At any time only one thread should do computation for one set of
> > params
> > * Can be a memory leak if misused
> > * Absolutely NOT a substitute for proper caching
>
> > Here's prototype delomboked output:https://gist.github.com/2690759.
>
> > I've cloned the git repo and stubbed out the prototype for Javac.  It
> > was quite a struggle understanding the sun compiler api without any
> > documentation.  I hope Ecj will be simpler.  Following test
> >https://gist.github.com/2690898seems to be working right now.

ka

unread,
May 14, 2012, 7:21:20 AM5/14/12
to Project Lombok
We could give an option to choose soft refs. But it might be a
problem if the user somehow depends on memoization behavior of the
method.

On May 14, 1:51 pm, Tumi <elbailedelmil...@gmail.com> wrote:
> In order to avoid memory leaks / empty the "cache" when needed a GC,
> SoftReference shoud be used, so instead of:
>     private final java.util.concurrent.ConcurrentHashMap
> __lombok_test_c_0 = new java.util.concurrent.ConcurrentHashMap();
>
> It could be be:
>     private final SoftReference<ConcurrentHashMap> __lombok_test_c_0 =
> new new SoftReference<ConcurrentHashMap>(new ConcurrentHashMap());
>
> P.S: "SoftReference<Map<K,V>>" is preferred over "Map<K,
> SoftReference<V>>" in terms of GC time/work, seehttp://www.ibm.com/developerworks/java/library/j-jtp01246/index.html
> > >https://gist.github.com/2690898seemsto be working right now.

ka

unread,
May 14, 2012, 7:37:21 AM5/14/12
to Project Lombok
Just a note, the current implementation doesn't work with soft ref
map. If the map gets gced when some threads are waiting, it will
totally break.

Maybe we could provide an option for user to provide his own cache
implementation (for eg. guava soft cache) if needed. But in that
case, its already getting too complicated and @Memoize does not
provide that much value.
> > > >https://gist.github.com/2690898seemstobe working right now.

rjee

unread,
May 21, 2012, 2:57:13 PM5/21/12
to Project Lombok

I've discussed this with Reinier and Roel and at the moment we see a
lot of things that need to be solved.
- We're absolutely on the fence if this should be threadsafe by
default. Isn't this the same argument as Vector v.s. ArrayList? Should
this be configurable?
- ConcurrentHashMap seems like an enormous overhead to safeguard
thread safety; in fact, in many cases the cost of recalculating (or
requerying) is lower, defeating the point.
- Both using a SoftReference of a Map or a Map of SoftReferences has
big downsides. The former will happily cache and give a big
performance spike when it is collected, the latter uses a *lot* of
tracker objects.
> > > > >https://gist.github.com/2690898seemstobeworking right now.

Maaartin G

unread,
May 21, 2012, 6:09:45 PM5/21/12
to project...@googlegroups.com
On Monday, May 21, 2012 8:57:13 PM UTC+2, rjee wrote:

I've discussed this with Reinier and Roel and at the moment we see a
lot of things that need to be solved.
- We're absolutely on the fence if this should be threadsafe by
default. Isn't this the same argument as Vector v.s. ArrayList?

IMHO not. Synchronizing Vector was a mistake since it's a too basic structure and since the JIT wasn't able to optimize away needless synchronization.
 
Should this be configurable?

Probably yes. Sometimes you can't afford the thread-safety, sometimes you have to. A thread-safe method used on many places makes non-thread-safe @Memoize useless.
 
- Both using a SoftReference of a Map or a Map of SoftReferences has
big downsides. The former will happily cache and give a big
performance spike when it is collected, the latter uses a *lot* of
tracker objects.

Actually using anything like this instead of a proper cache seems to be wrong, given bugs like
and the flexibility of caches like

I see no nice solution:
- The above bug may render SoftReference-s simply unusable. 
- There's no cache in JDK itself and making Lombok depend on any library (even such a nice one as Guava) is AFAIK not an option.
- Forcing the user to specify the kind of cache and its parameters in each @Memoize makes it too long to be appealing.
- Using any magic, like e.g., looking for a class called CacheBuilder in the current package, is simply too much magic.
- Allowing the user to specify this more globally (e.g., per package) would be nice but it's AFAIK impossible given the way Lombok works.

ka

unread,
May 22, 2012, 1:57:01 PM5/22/12
to Project Lombok
My 2 cents:

I agree with everything Maaartin says, there's no nice simple
solution.

We could make thread safety optional, defaulting to non thread safe.
But since @Memoize probably wont be used in perf. sensitive code, I
don't think thread safety is a very big deal.

This should absolutely not be a replacement for caching. Caching is a
hard problem with too many parameters. Lets keep @Memoize simple and
stupid.

On May 22, 3:09 am, Maaartin G <grajc...@seznam.cz> wrote:
> On Monday, May 21, 2012 8:57:13 PM UTC+2, rjee wrote:
>
> > I've discussed this with Reinier and Roel and at the moment we see a
> > lot of things that need to be solved.
> > - We're absolutely on the fence if this should be threadsafe by
> > default. Isn't this the same argument as Vector v.s. ArrayList?
>
> IMHO not. Synchronizing Vector was a mistake since it's a too basic
> structure and since the JIT wasn't able to optimize away needless
> synchronization.
>
> > Should this be configurable?
>
> Probably yes. Sometimes you can't afford the thread-safety, sometimes you
> have to. A thread-safe method used on many places makes non-thread-safe
> @Memoize useless.
>
> > - Both using a SoftReference of a Map or a Map of SoftReferences has
> > big downsides. The former will happily cache and give a big
> > performance spike when it is collected, the latter uses a *lot* of
> > tracker objects.
>
> Actually using anything like this instead of a proper cache seems to be
> wrong, given bugs likehttp://bugs.sun.com/bugdatabase/view_bug.do;jsessionid=cfd518f51afc77...
> and the flexibility of caches likehttp://docs.guava-libraries.googlecode.com/git/javadoc/com/google/com...

ka

unread,
Jul 1, 2012, 12:37:28 PM7/1/12
to Project Lombok
Hey guys any progress here ? Our project would love to see this in
Lombok !

On May 22, 10:57 pm, ka <sancha...@gmail.com> wrote:
> My 2 cents:
>
> I agree with everything Maaartin says, there's no nice simple
> solution.
>
> We could make thread safety optional, defaulting to non thread safe.
> But since @Memoizeprobably wont be used in perf. sensitive code, I
> don't think thread safety is a very big deal.
>
> This should absolutely not be a replacement for caching.  Caching is a
> hard problem with too many parameters.  Lets keep @Memoizesimple and
> > @Memoizemakes it too long to be appealing.

Reinier Zwitserloot

unread,
Jul 2, 2012, 4:13:18 PM7/2/12
to project...@googlegroups.com
This is not an itch we want to scratch.

In general we've added a bunch of features that we feel are hardly if ever used, vs. a bunch that see extensive use. (As measured by the speed at which bugs are reported against these features, vs. the evident likelyhood of people running into such bugs with common use).

We'd like to work on the first kind: val, @Data and friends, @Cleanup. We'd like to work less on features of the second kind: @Getter(lazy=true), @Synchronized, @Delegate. As cool as they are, lack of use means they aren't as stable as they ought to be.

More to the point, perhaps: I don't foresee myself using this, which means working on it is not a particularly exciting proposition for me. Roel and Robbert Jan feel the same way. We don't get paid for this :)

A patch would probably be accepted (into .experimental), but post the _EXACT_ code that you intend to generate first for review.

Fabrizio Giudici

unread,
Jul 3, 2012, 5:14:00 AM7/3/12
to project...@googlegroups.com, Reinier Zwitserloot
On Mon, 02 Jul 2012 22:13:18 +0200, Reinier Zwitserloot
<rein...@gmail.com> wrote:


> We'd like to work on the first kind: val, @Data and friends, @Cleanup.
> We'd
> like to work less on features of the second kind: @Getter(lazy=true),
> @Synchronized, @Delegate. As cool as they are, lack of use means they
> aren't as stable as they ought to be.

Are you sure your metric is ok? Because, for instance:

1. I'm using @Delegate a lot, but it's ok so I've never filed an issue on
it
2. I could numerically use @Delegate much less than @Getter for obvious
reasons, since they apply to different granularity in the code.
Nevertheless @Delegate saves to me much more code and annoyances than
@Getter.

Just to make another quick example, a couple of days ago I had to patch
Spring so the property to point to XML beans files is not hardwired in
web.xml, but it's computed at runtime in function of a system property. To
accomplish that I had to subclass ContextLoaderListener so I can feed it a
patched property "contextConfigLocation". Unfortunately, it doesn't expose
a setter method for that, so I have to "fool" it by
passing a decorator of ServletContext, from which it reads the property by
means of getInitParameter(). ServletContext can't be subclassed, since
it's instantiated by the webcontainer: only decorating it can work.

For the curious one, code is below (ok, I should blog about it).


This pattern of customization things that doens't expose a setter method
happens relatively often and the solution is to provide a decorator of the
original object exposing the property / behaviour that I want to change.
Now, @Delegate is not only invaluable because of the code it saves me, but
above all it's a time saver whem I'm prototyping (often different
alternate solutions because at the beginning I don't have a clear path in
my mind).



Probably to better measure popularity of a Lombok annotation we should set
up a poll (with well designed questions).





@RequiredArgsConstructor
class DynamicConfigLocationServletContext implements ServletContext {
public static final String CONTEXT_CONFIG_LOCATION =
"contextConfigLocation";

interface Exclusions {
public String getInitParameter(String string);
}

@Nonnull @Delegate(excludes=Exclusions.class)
private final ServletContext delegate;

@Nonnull
private final String contextConfigLocation;

@Override
public String getInitParameter(final @Nonnull String name) {
return name.equals(CONTEXT_CONFIG_LOCATION) ? contextConfigLocation
:
delegate.getInitParameter(name);
}
}

public class DynamicConfigLocationContextLoaderListener extends
ContextLoaderListener {

@Override
public void contextInitialized(ServletContextEvent event)
final ServletContext servletContext = event.getServletContext();
final String contextConfigLocation =
servletContext.getInitParameter(DynamicConfigLocationServletContext.CONTEXT_CONFIG_LOCATION)
+ ",classpath*:/beans/*" + getCustomization() +
"*Beans.xml";

final ServletContext servletContextDelegate = new
DynamicConfigLocationServletContext(servletContext, contextConfigLocation);
super.contextInitialized(new
ServletContextEvent(servletContextDelegate));
}

@Nonnull
private String getCustomization() {
...
}
}




--
Fabrizio Giudici - Java Architect, Project Manager
Tidalwave s.a.s. - "We make Java work. Everywhere."
fabrizio...@tidalwave.it
http://tidalwave.it - http://fabriziogiudici.it

eric.giese

unread,
Jul 5, 2012, 3:11:48 AM7/5/12
to Project Lombok
Reinier's argument is pretty strong in my opinion:

The features of lombok are defined by two different factors:
- usage (esp. within the lombok team itself)
- complexity of implementation

Higher complexity means more maintenance overhead for the lombok team
and a higher probability of the feature to be incompatible with future
java compilers. High complexity features are IMO val and delegate.

Highly used features get quicker bug reports and, well, a higher
usage, so they seem to be more useful after all. As lombok directly
hacks into the javac, only extremly highly used features deserve an
existence. After all, if a feature is rarely used you can still auto-
generate the boilerplate code in eclipse and be done with it.

@Data and friends are easy to implement and highly used.
- val is complex but highly used, so it surely is worth the trouble
@LogXYZ is simple and highly used
@Delegate is moderately useable, but very hard to implement
@Synchronized is easy to implement, but rarely useable.
@Cleanup is straightforward and very useful for now but will fall out
of grace when JDK 7 is broadly used in ~ 5 years :-)

Philip Healy

unread,
Jul 5, 2012, 8:20:04 AM7/5/12
to project...@googlegroups.com
@Delegate isn't getting the love it deserves in this thread!

I am a big fan of this feature because it addresses one of the particularly annoying shortcomings of Java: lack of multiple inheritance or mixins.

Like Fabrizio, I use it frequently, and I hope that it is deprecated only if absolutely necessary.

As regards frequency of use: val, @Data, @Log and other popular features can be used in practically any Java class.  @Delegate solves a very specific issue that arises in only a small number of classes, but is absolutely invaluable when delegation is required.  IDE-generated boilerplate is a poor substitute as Eclipse will generate the method stubs but not the calls to the delegate class, and the resulting boilerplate is particularly obnoxious.

This is exactly the type of problem that Lombok should be solving!

P.






--
You received this message because you are subscribed to the Google
Groups group for http://projectlombok.org/

To post to this group, send email to project...@googlegroups.com
To unsubscribe from this group, send email to
project-lombo...@googlegroups.com
For more options, visit this group at
http://groups.google.com/group/project-lombok?hl=en

Maaartin G

unread,
Jul 5, 2012, 9:03:34 AM7/5/12
to project...@googlegroups.com
On Thursday, July 5, 2012 2:20:04 PM UTC+2, philip...@gmail.com wrote:
As regards frequency of use: val, @Data, @Log and other popular features can be used in practically any Java class.  @Delegate solves a very specific issue that arises in only a small number of classes, but is absolutely invaluable when delegation is required.  IDE-generated boilerplate is a poor substitute as Eclipse will generate the method stubs but not the calls to the delegate class, and the resulting boilerplate is particularly obnoxious.

Using Eclipse Source -> Generate Delegate Methods does the job correctly, at least for me.  That said, I agree that it's a terrible source code pollution.

Philip Healy

unread,
Jul 5, 2012, 9:28:42 AM7/5/12
to project...@googlegroups.com
So it does, today I learned!

But now that I have had access to @Delegate for a while it would be depressing to go back to writing boilerplate delegate calls...

P.



Reinier Zwitserloot

unread,
Jul 6, 2012, 7:16:30 AM7/6/12
to project...@googlegroups.com
Okay, okay, I surrender! @Delegate more important than I thought :)

NB: I intend to stick with @Cleanup because ARM blocks have so many wonky annoyances. It's broken as designed for starters (adding an exception to itself as a cause causes an exception, so if some I/O layer actually does the right thing and rethrows the original exception if you attempt to close a stream that is already throwing exceptions, an ARM block will end up with an IllegalArgumentException instead of the IOException. Fail. I also like the API of @Cleanup more. It's simpler. If we can add the cause business ourselves (with smarts to avoid the above broken-as-designed issue) it'd be perfect.

On Thursday, July 5, 2012 3:28:42 PM UTC+2, philip...@gmail.com wrote:
So it does, today I learned!

But now that I have had access to @Delegate for a while it would be depressing to go back to writing boilerplate delegate calls...

P.



On 5 July 2012 14:03, Maaartin G <graj...@seznam.cz> wrote:
On Thursday, July 5, 2012 2:20:04 PM UTC+2, philip...@gmail.com wrote:
As regards frequency of use: val, @Data, @Log and other popular features can be used in practically any Java class.  @Delegate solves a very specific issue that arises in only a small number of classes, but is absolutely invaluable when delegation is required.  IDE-generated boilerplate is a poor substitute as Eclipse will generate the method stubs but not the calls to the delegate class, and the resulting boilerplate is particularly obnoxious.

Using Eclipse Source -> Generate Delegate Methods does the job correctly, at least for me.  That said, I agree that it's a terrible source code pollution.

--
You received this message because you are subscribed to the Google
Groups group for http://projectlombok.org/
 
To post to this group, send email to project-lombok@googlegroups.com

To unsubscribe from this group, send email to

Ryan Schmitt

unread,
Jul 4, 2015, 6:57:10 PM7/4/15
to project...@googlegroups.com
In the case of @Delegate functionality, runtime bytecode generation using invokedynamic is also a viable strategy. I prototyped this idea, but I've yet to pursue it further:

https://github.com/bdonlan/invokedynamic-proxy/pull/7

The code sample showing what this would actually look like is here:

https://raw.githubusercontent.com/rschmitt/invokedynamic-proxy/delegate/lib/src/test/java/net/fushizen/invokedynamic/proxy/DelegateFactoryTest.java

I'm not sure which technique (Lombok or runtime code generation) is a better fit for this specific feature. I looked into the "issues with generics erasure" that Lombok encountered, but I don't remember what they were or whether they affected my prototype.


On Friday, July 6, 2012 at 4:16:30 AM UTC-7, Reinier Zwitserloot wrote:
Okay, okay, I surrender! @Delegate more important than I thought :)

NB: I intend to stick with @Cleanup because ARM blocks have so many wonky annoyances. It's broken as designed for starters (adding an exception to itself as a cause causes an exception, so if some I/O layer actually does the right thing and rethrows the original exception if you attempt to close a stream that is already throwing exceptions, an ARM block will end up with an IllegalArgumentException instead of the IOException. Fail. I also like the API of @Cleanup more. It's simpler. If we can add the cause business ourselves (with smarts to avoid the above broken-as-designed issue) it'd be perfect.

On Thursday, July 5, 2012 3:28:42 PM UTC+2, philip...@gmail.com wrote:
So it does, today I learned!

But now that I have had access to @Delegate for a while it would be depressing to go back to writing boilerplate delegate calls...

P.



On 5 July 2012 14:03, Maaartin G <graj...@seznam.cz> wrote:
On Thursday, July 5, 2012 2:20:04 PM UTC+2, philip...@gmail.com wrote:
As regards frequency of use: val, @Data, @Log and other popular features can be used in practically any Java class.  @Delegate solves a very specific issue that arises in only a small number of classes, but is absolutely invaluable when delegation is required.  IDE-generated boilerplate is a poor substitute as Eclipse will generate the method stubs but not the calls to the delegate class, and the resulting boilerplate is particularly obnoxious.

Using Eclipse Source -> Generate Delegate Methods does the job correctly, at least for me.  That said, I agree that it's a terrible source code pollution.

--
You received this message because you are subscribed to the Google
Groups group for http://projectlombok.org/
 
To post to this group, send email to project...@googlegroups.com

To unsubscribe from this group, send email to
Reply all
Reply to author
Forward
0 new messages