memory-saving tips

topic posted Wed, September 15, 2004 - 11:51 PM by  offlineMike
Share/Save/Bookmark
Advertisement

Does anyone have any clever memory-saving tips to share?

The app I've been working on has been bumping up against the amount of memory we have, so I've come up with a few strategies that have helped alleviate the problem. Here they are...

1) Pack booleans into an int.

One of our classes has a bunch of boolean fields. Each boolean field is inefficiently stored in a 32-bit int. Now we just have one int field, which can hold all of the booleans.

The code looks like this...

<pre>
public Item {
// member variable for holding booleans
public int itemBooleans;

// utility for setting boolean values in the int
private void setItemBoolean(int pos, boolean val) {
int powerOfTwo = 1 << pos;
if (val)
this.itemBooleans |= powerOfTwo;
else
this.itemBooleans &= (Integer.MAX_VALUE-powerOfTwo);
}

// utility method for extracting boolean values out of the int
private boolean getItemBoolean(int pos) {
int powerOfTwo = 1 << pos;
return ((this.itemBooleans & powerOfTwo) != 0);
}

public void setIsRound(boolean b) {
setItemBoolean(0, b);
}

public boolean getIsRound() {
return getItemBoolean(0);
}

public void setIsSquare(boolean b) {
setItemBoolean(1, b);
}

public boolean getIsSquare() {
return getItemBoolean(1);
}
}
</pre>


2) Pack Dates into ints.

My first observation was that it's wasteful to store a Date object when all you really need to do is store the long value that the Date wraps. The class's API should use Date objects, but internally the Dates should be stored as longs.

My second observation is that if you only need accuracy to the nearest second, instead of to the nearest millisecond, then you don't even need a long. An int works just fine.

The code looks like this:

<pre>
import java.util.Date;

public abstract class DateUtils {

public static Date intToDate(int time) {
return (time != 0) ? new Date((long)time * 1000) : null;
}

public static int dateToInt(Date date) {
return (date != null) ? ((int) (date.getTime() / 1000)) : 0;
}
}


public class Item {
// member varibale that holds a date value
int creationDate;

public void setCreationDate(Date creationDate) {
this.creationDate = DateUtils.dateToInt(creationDate);
}

public Date getCreationDate() {
return DateUtils.intToDate(this.creationDate);
}
}
</pre>
posted by:
Mike
SF Bay Area
Advertisement
Advertisement
  • Re: memory-saving tips

    Thu, September 16, 2004 - 8:26 AM
    (hi Mike!)

    one thing I did here, which saves on memory although the biggest savings is in speed, is to implement lazy-loading of member objects. our website templating tool had about 20 or 30 member objects (each at least one additional database query) every time a common object was instantiated, even though not all of those members were used in every instantiation. since that common object was instantiated thousands of times in any given user session, it sped up the app by a factor of 5 or 10 and reduced the memory as well as the latency.

    I'd recommend something like Hibernate for that, but it was overkill for what we needed and I couldn't get it working fast enough in our environment, so I rolled my own with introspection.
  • Unsu...
     

    Re: memory-saving tips

    Thu, September 16, 2004 - 10:00 AM


    That's a pretty nifty trick. Usually, when I run into memory problems, I'll approach it from a higher-level OOA/OOD to reduce the footprint. Are you creating a lot of objects that you can use the Flyweight pattern to reduce their size?
    • Re: memory-saving tips

      Thu, September 16, 2004 - 1:24 PM
      I'm not actually using the Flyweight pattern. Most of the fine-grained data fields in my business objects are Strings and primitive values (especially now that I have Dates stored as ints). The fine-grained data fields that are non-String objects are mostly pointing at objects in a cache, and my caching strategy does a pretty good job of preventing having multiple copies of the same object in memory. For the Strings, occasionally I use a collection of Strings to hold one copy each of frequently-used String values, and I probably should be doing more of that.
      • Ken
        Ken
        offline 2

        Re: memory-saving tips

        Thu, September 16, 2004 - 5:29 PM
        Are you running out of physical memory, or JVM memory?? If it's JVM memory, why not modify the JVM parameters. Doing neat coding tricks for the sake of coding is fine, but if this is a real coding 'job' where you're getting paid by the hour, it's much more cost effective for them to bite the bullet and buy some more system memory.

        Even if you work cheap, by the time you get all these tips in place and tested, you've eaten up about as much money as it would have taken to get some more memory for the box. (If that's what's necessary).

        Otherwise, I'd like to know more about what the app does and such to make recommendations on memory saving. Object re-use would be helpful, but I don't know what the objects in your system are/do.

        Another thought would be to pull down one of the tools for metering/measuring code. JProbe (commercial, but could probably get a 30 day trial version), and other OSS memory and performance monitors.

        If the string values don't change, turn them into public static final strings, and let the system handle them instead of instantiating them in 'string' memory.

        There's lots of other little things, they just depend on what you're trying to do with the program.

        Making sure you release all the resources you can, and tweaking the JVM to try to get the garbage collection parameters to call garbage collecting a little more often may free up some memory, possibly. GC is a pretty unreliable way to get more memory, but every little tweak helps.

        I agree with the lazy initialization mentioned earlier. Don't instantiate objects that you don't need to, and structure the logic in your methods to bail out as soon as they hit an error condition (keeping you from continuing through the method possibly creating objects you won't need.)

        Lots of little things.
        • Re: memory-saving tips

          Fri, September 17, 2004 - 1:14 PM
          Performance is definitely a consideration as is initial development costs, but, in my book, even bigger than these two is readability / maintenance / flexibility - if this is sacrificed, your overall project cost can skyrocket in the long term. (very simple e.g. related to storing dates as ints/longs - client now says, "oh, we also want to store timezone info..." eek!)

          You can't assume that you will be working on the code from cradle to grave, nor can you assume that you will even remember the coding details a year or two from now!

          Part of the definition of "project nightmare" for me is to inherit a whole bunch of "cleverly-done" code. (Well, that and "uhmm, we've already told them that it would be done by the end of the month." hee hee) I'd much prefer a straight-up, by-the-book implementation.
          • Re: memory-saving tips

            Fri, September 17, 2004 - 3:55 PM
            I think I have a different idea of a "nightmare project" than you do, Smasharolla.

            The worst code that I've had to deal with usually is written by developers who don't understand the subtleties of the environment they are programming in -- not using inheritance or interfaces when it makes sense to, not encapsulating repetitive code in a class or method, spaghetti servlets/JSPs, database queries without the correct supporting indices, etc.

            I do agree with you that stuff that is too clever is often not worth the bother.

            But in defense of packing Dates into ints... It's only done in a single class, and affects no other classes in the system (since the class's API uses Dates). The code that translates from int to Date and back is *really* short and simple. And if some need appeared in the future that required us to use actual Date objects, the code could be changed back to using Dates in about 10 minutes.

            Packing Dates into ints gives me a memory savings of about 6-to-1. Packing booleans gives me a memory savings of about 30-to-1. Given the simplicity of the change and how well encapsulated it is, it's definite a good thing for my app.
            • Ken
              Ken
              offline 2

              Re: memory-saving tips

              Fri, September 17, 2004 - 5:34 PM
              Both situations are nightmares, just different types. Spaghetti code is horrible to have to change, and 'cute' solutions are hard to follow unless you are the one that created them originally, and only if it hasn't been 6 months and you've forgotten why you coded it that way.

              Plain vanilla coding, using design patterns and standard coding conventions makes for the best updatable code. And if it solves the problem set adequately, it doesn't necessarily have to be fancy.

              Complex code tends to be more fragile than simple code in a number of ways.

              I always tell my team; "I'm a big fan of simple"

              Having said that, if your date stuff is that efficient, there's no reason you can't encapsulate it in an anonymous inner class so there's no outside visibility if you don't want there to be.

              Or create your own date class by extending the existing date class (don't remember off the top of my hand if they are final classes or not, so don't flame me) that does automatic conversions internally. Looks like a date, acts like a date, but inside it's an int.

              Thinking of different ways to remove outer fur layer from feline.
        • Re: memory-saving tips

          Fri, September 17, 2004 - 3:35 PM
          Good questions, Ken... (Wow, the service here is great!)

          The machine we're using has 3 Gig of RAM, and the JVM is using 2 Gig of that. One of our developers tried giving the JVM (Sun's jdk 1.5.0 beta on Linux 2.6.5) more than 2 Gig of RAM, and weird bugs started appearing during testing. Definitely something to look into.

          The app itself is a hosted service for project collaboration. (The company's web site is www.linkify.com, but it focuses more on how the customer benefits from the software rather what the software actually does.)

          The data set mostly consists of projects. A project consists of rollups and tasks (like in Microsoft Project), and any rollup or task can contain other things such as discussions or a document repository (a folder hierarchy for storing files). All of the business objects referred to in the previous sentence are implemented using our "Item" base class. Many of the projects contain over 100,000 Item objects. Since response time has to be quick, we can't afford too many trips to the database, so we cache almost everything in memory -- only kicking out the least recently used objects when absolutely necessary.

          Regarding your suggestions...

          1) We do use static final Strings and other static final objects whenever it makes sense to.

          2) I can see how object re-use might improve performance by eliminating the CPU time spent on memory allocation and meory free-ing. But how does object re-use save on memory?

          3) Thanks for the tip on tweaking the garbage collection parameters. That hadn't occured to me. I see that Sun has a "Tuning Garbage Collection" web page. DO you have any additional insights to offer on this topic?

          4) Lazy initialization -- yes, already doing that.
          • Ken
            Ken
            offline 2

            Re: memory-saving tips

            Fri, September 17, 2004 - 5:26 PM
            We aim to please. (You aim too please.)

            Object re-use in it's roughest form prevents the creation of new objects taking up more memory, by re-using existing objects you save in memory and creation/destruction time, and don't have to depend on the garbage collection to clear out the deleted objects from memory. Remember, you don't get any more memory until GC actually runs, and that can be anytime. Better to create only what you need and re-use things if possible. The singleton pattern is excellent for all utility classes in this respect.

            I don't have any specific info on the GC tuning, sorry.

            Another couple of things you might want to consider. Using another JVM altogether. I'd be a little nervous about using a beta JVM for production systems. There's a reason they're called BETA <Grin>. There are other JVMs out there that might be better suited and more stable with the setup you've got. If there is nothing you're using specific to the 1.5 JDK, I'd try switching the JVM just to see.

            It sounds like your only other option is to start looking at a distributed model where you can throw separate boxes at the problem. That's an awful lot of objects you're talking there, and even using the flyweight pattern, there's a minimum amount of overhead in instantiating that many objects, even if they do absolutely nothing.

            You might be caching too much in memory. Maybe add some logging code so you can capture the actual hit/miss ratio and tune exactly how much is in memory. It's better to have a little lag time than have the application crash.

            BTW, couldn't get to www.linkify.com. Don't know why.
          • Unsu...
             

            Re: memory-saving tips

            Tue, September 21, 2004 - 1:01 PM

            2 Gb and you're running out of memory??? I'd bet good money there are some other issues, like memory loitering, that ought to be addressed before resorting to tricks like packing booleans into ints.
            • Ken
              Ken
              offline 2

              Re: memory-saving tips

              Tue, September 21, 2004 - 5:25 PM
              I agree, but I seem to remember him saying something about 100k objects or some large number. That'd be my first point to see if I couldn't eliminate some of those in-memory objects.
            • Re: memory-saving tips

              Wed, September 22, 2004 - 1:41 PM
              I would do some profiling to see where your memory is going. JProbe has free trial licenses available on their website, but be forewarned that enabling the profiling slows down your app by an order of magnitude or more. Not so good for production debugging.

              One thing we've found useful is to enable verbose GC, piping the output to a script to prefix timestamps, and then plotting those trends over time. If you also have timestamped entries for other events in your system (such as when transactions start, when users log on/off, etc), you can correlate them fairly easily using something like perl and excel. It won't tell you what objects are sticking around, but it can show you if you have a leak and what it may be related to.
          • Re: memory-saving tips

            Wed, September 22, 2004 - 1:14 PM
            "Many of the projects contain over 100,000 Item objects. Since response time has to be quick, we can't afford too many trips to the database, so we cache almost everything in memory -- only kicking out the least recently used objects when absolutely necessary."

            therein lies the problem...

            1.) well designed databases are really fast these days
            2.) caching everything, even if the data is accessed only say, once a day, is prohibitively inefficient

            any tweaks you make to data types are incremental at best (since you're cache is going to fill up and stay full anyway)

            I'm guessing that you should revisit the app design, the database design, and the information architecture. If you do these three things well, memory should not be an issue when you have a 2GB sandbox to play in.
  • _
    _
    offline 3

    Re: memory-saving tips

    Sat, September 18, 2004 - 9:40 AM
    Re: #1

    Are you sure that is going to work on the 31st position? (i.e. 1<<31).

    I think maybe you want:

    this.itemBooleans &= ~powerOfTwo;

    instead

Recent topics in "Java Monkeys"

Topic Author Replies Last Post
Javascripting costs Schirin 3 May 17, 2007
Design patterns and principles emblylan 6 February 14, 2007
open source hosting alternatives? Mark 2 February 3, 2007
Ruby > Java Unsubscribed 6 January 9, 2007