Hi there,

Why not implement such a thing as a separate library/class. After all,
Map is an interface which can be implemented in many ways and for many
different purposes. I think there are a couple of efforts that go in
this direction, for example javolution:

http://javolution.org/

Cheers, Roman

Am Mittwoch, den 21.11.2007, 09:37 -0800 schrieb Martin Buchholz:
> Hi Peter,
> 
> It's true that under low memory conditions your code would
> allow execution to continue under some circumstances.
> However, I'm not sure this would be an improvement to the JDK.
> Recovery from OOME is fraught with hazards.  We do occasionally
> try, but an application becomes much less reliable once OOMEs
> start appearing.  Perhaps it's better to fail than to pretend
> that the JDK has been bullet-proofed against OOME.
> OOME recovery code is rarely executed and hard to test.
> The new code would have to be maintained indefinitely,
> making future maintenance just a little bit harder for
> the maintainers.
> 
> If the hashmap is fully populated, most of the memory is tied
> up in the Entry objects themselves, not in the table array.
> Each Entry object should be about 5 words of memory, while
> there's approximately one word used within the table array.
> So I don't think we'll see anything close to the factor of
> two max memory saving that we might expect.
> 
> I would prefer to see engineering work go into something
> like auto-reduction of the table array when many elements
> have been removed, but that's a hard problem.
> 
> Martin
> 
> Peter Arrenbrecht wrote:
> > Hi all,
> > 
> > I recently thought about how to resize hashmaps. When looking at the
> > JDK 6 source, I saw that java.util.HashMap always transfers old
> > entries from the old table. When memory is tight, it might be better
> > to first concatenate the entry lists into one big list, throw away the
> > old table, allocate the new one, and then fill it from the
> > concatenated list. So:
> > 
> >     @Override
> >     void resize( int _newCapacity )
> >     {
> >             try {
> >                     fastResize( _newCapacity ); // This would be the 
> > current resize code.
> >             }
> >             catch (OutOfMemoryError e) {
> >                     tightResize( _newCapacity );
> >             }
> >     }
> > 
> >     @SuppressWarnings( "unchecked" )
> >     private void tightResize( int newCapacity )
> >     {
> >             Entry head = joinLists();
> >             table = new Entry[ newCapacity ];
> >             threshold = (int) (newCapacity * loadFactor);
> >             transfer( head, table );
> >     }
> > 
> >     @SuppressWarnings("unchecked")
> >     private Entry joinLists()
> >     {
> >             final Entry[] src = table;
> >             final int n = src.length;
> >             Entry head = null;
> >             int i = 0;
> >             while (i < n && null == head) {
> >                     head = src[ i++ ];
> >             }
> >             Entry tail = head;
> >             assert i >= n || null != tail;
> >             while (i < n) {
> >                     Entry e = src[ i++ ];
> >                     if (null != e) {
> >                             tail.next = e;
> >                             do {
> >                                     tail = e;
> >                                     e = e.next;
> >                             } while (null != e);
> >                     }
> >             }
> >             return head;
> >     }
> > 
> >     @SuppressWarnings("unchecked")
> >     private void transfer( Entry head, Entry[] tgt )
> >     {
> >             int n = capacity();
> >             while (head != null) {
> >                     Entry e = head;
> >                     head = head.next;
> >                     int i = indexFor( e.hash, n );
> >                     e.next = tgt[ i ];
> >                     tgt[ i ] = e;
> >             }
> >     }
> > 
> > 
> > What do you think?
> > -peo
> E-Mail-Nachricht-Anlage (Attached Message)
> > -------- Weitergeleitete Nachricht --------
> > Von: Peter Arrenbrecht <[EMAIL PROTECTED]>
> > Antwort an: [EMAIL PROTECTED]
> > An: core-libs-dev@openjdk.java.net
> > Betreff: Re: Proposal: Better HashMap.resize() when memory is tight
> > Datum: Wed, 21 Nov 2007 16:51:28 +0100
> > 
> > einfaches Textdokument-Anlage (Attached Message)
> > Hi again, I have gone over my code again and a) discovered a very
> > stupid mistake rendering the desired effect null and void, and b)
> > developed a test that demos the effect of the improvement. Here's the
> > improved code:
> > 
> >     private void tightResize( int newCapacity )
> >     {
> >             Entry head = joinLists();
> >             table = null; // free it first
> >             table = new Entry[ newCapacity ]; // then reallocate
> >             threshold = (int) (newCapacity * loadFactor);
> >             transfer( head, table );
> >     }
> > 
> > Below you can find the test code. This shows the problem here on
> > Ubuntu Linux 7.04 with jre 1.6.0:
> > 
> > java version "1.6.0"
> > Java(TM) SE Runtime Environment (build 1.6.0-b105)
> > Java HotSpot(TM) Server VM (build 1.6.0-b105, mixed mode)
> > 
> > The command-line for the improved map is:
> > 
> >   java -server -Xmx7m -cp bin ch.arrenbrecht.java.util.BetterHashMap new
> > 
> > and for the old map:
> > 
> >   java -server -Xmx7m -cp bin ch.arrenbrecht.java.util.BetterHashMap
> > 
> > I have only managed to demo the effect on the server VM. And it is
> > necessary to leave an Object array of size initial*2+something free,
> > rather than just initial+something, which I expected. Maybe that is an
> > effect of the generational collector. Also, there have been spurious
> > cases where the test failed even with the new map. No idea why.
> > 
> > Here is the test code:
> > 
> >     public static void main( String[] args )
> >     {
> >             final int initial = 131072;
> >             final float loadFactor = 0.5F;
> >             final HashMap<Integer, Integer> m;
> >             if (args.length > 0) {
> >                     System.out.println( "Creating better map..." );
> >                     m = new BetterHashMap<Integer, Integer>( initial, 
> > loadFactor );
> >             }
> >             else {
> >                     System.out.println( "Creating standard map..." );
> >                     m = new HashMap<Integer, Integer>( initial, loadFactor 
> > );
> >             }
> > 
> >             System.out.println( "Priming map (should see no resize 
> > here)..." );
> >             for (int i = 0; i < initial / 2; i++) {
> >                     Integer o = i;
> >                     m.put( o, o );
> >             }
> >             Integer o = initial;
> > 
> >             Entry head = blockMemExcept( initial * 2 + initial / 4 );
> >             System.out.println( "Filled with " + n + " entries." );
> > 
> >             System.out.println( "Adding next element (should see resize 
> > here)..." );
> >             m.put( o, o );
> > 
> >             if (head == null) System.out.println( "Bad." ); // force "head" 
> > to
> > remain in scope
> >             System.out.println( "Done." );
> >     }
> > 
> >     /**
> >      * Done separately so memBlock goes out of scope cleanly, leaving no
> > local stack copies pointing
> >      * to it.
> >      */
> >     private static Entry blockMemExcept( int exceptObjs )
> >     {
> >             System.out.println( "Reserving memory..." );
> >             Object[] memBlock = new Object[ exceptObjs ];
> > 
> >             System.out.println( "Filling rest of memory..." );
> >             int i = 0;
> >             Entry head = null;
> >             try {
> >                     while (true) {
> >                             head = new Entry( 0, null, null, head );
> >                             i++;
> >                     }
> >             }
> >             catch (OutOfMemoryError e) {
> >                     // ignore
> >             }
> > 
> >             if (memBlock[ 0 ] != null) return null;
> >             n = i;
> >             return head;
> >     }
> >     private static int n = 0;
> > 
> > Cheers,
> > -peo
> > 
> > 
> > ps. This all runs on copies of HashMap and AbstractMap in
> > ch.arrrenbrecht.java.util.
> > 
> > 
> > On Nov 21, 2007 9:52 AM, Peter Arrenbrecht <[EMAIL PROTECTED]> wrote:
> > > Hi all,
> > >
> > > I recently thought about how to resize hashmaps. When looking at the
> > > JDK 6 source, I saw that java.util.HashMap always transfers old
> > > entries from the old table. When memory is tight, it might be better
> > > to first concatenate the entry lists into one big list, throw away the
> > > old table, allocate the new one, and then fill it from the
> > > concatenated list. So:
> > >
> > >         @Override
> > >         void resize( int _newCapacity )
> > >         {
> > >                 try {
> > >                         fastResize( _newCapacity ); // This would be the 
> > > current resize code.
> > >                 }
> > >                 catch (OutOfMemoryError e) {
> > >                         tightResize( _newCapacity );
> > >                 }
> > >         }
> > >
> > >         @SuppressWarnings( "unchecked" )
> > >         private void tightResize( int newCapacity )
> > >         {
> > >                 Entry head = joinLists();
> > >                 table = new Entry[ newCapacity ];
> > >                 threshold = (int) (newCapacity * loadFactor);
> > >                 transfer( head, table );
> > >         }
> > >
> > >         @SuppressWarnings("unchecked")
> > >         private Entry joinLists()
> > >         {
> > >                 final Entry[] src = table;
> > >                 final int n = src.length;
> > >                 Entry head = null;
> > >                 int i = 0;
> > >                 while (i < n && null == head) {
> > >                         head = src[ i++ ];
> > >                 }
> > >                 Entry tail = head;
> > >                 assert i >= n || null != tail;
> > >                 while (i < n) {
> > >                         Entry e = src[ i++ ];
> > >                         if (null != e) {
> > >                                 tail.next = e;
> > >                                 do {
> > >                                         tail = e;
> > >                                         e = e.next;
> > >                                 } while (null != e);
> > >                         }
> > >                 }
> > >                 return head;
> > >         }
> > >
> > >         @SuppressWarnings("unchecked")
> > >         private void transfer( Entry head, Entry[] tgt )
> > >         {
> > >                 int n = capacity();
> > >                 while (head != null) {
> > >                         Entry e = head;
> > >                         head = head.next;
> > >                         int i = indexFor( e.hash, n );
> > >                         e.next = tgt[ i ];
> > >                         tgt[ i ] = e;
> > >                 }
> > >         }
> > >
> > >
> > > What do you think?
> > > -peo
> > >
-- 
http://kennke.org/blog/

Reply via email to