James, Whilst make the collection synchronized is definitely not an option (as explained by Steven in the same thread), making the missing methois that access the collection(s) in a write mode is a goal.
We'd definitely appreciate your help in this context, and the more of a test environment you have, the better for us and yourself. Steven, shall we create a new Jira issue asking ourselves to have a look at this (making the remaining write methods thread-safe) ? Regards Werner > -----Ursprüngliche Nachricht----- > Von: [EMAIL PROTECTED] > [mailto:[EMAIL PROTECTED] Im Auftrag von James > Gesendet: Mittwoch, 21. März 2007 13:50 > An: [email protected] > Betreff: Re: Re: AW: AW: [castor-user] [XML] XMLClassDescriptorResolver > XMLFieldDescriptorImpl cache possible memory leak > > Hi All, > > I've just joined the mailing list because I saw a conversation between > Adam Chesney and the Castor development team in the archives, and > thought I would add my two cents. :) > > Below is a link to the last email on this thread: > > http://archive.castor.codehaus.org/user/00b501c767ea%24da8a1880%240a84010a > %40zultan > > We use Castor in much the same way as Adam: 100's of thousands of Castor > generated beans, and which get loaded at runtime depends very much on > the user/runtime profile of our application. I agree with Adam that > initialising the XMLClassDescriptorResolver at startup is not really a > useful solution - we would have to maintain a very messy (and error > prone) configuration to track individual bean classes in what is a huge > application, and it would also cause unnecessarily high memory usage. > > Although I can't be sure, looking at the source code for > XMLClassDescriptorResolverImpl.DescriptorCache in Castor 1.1, I think > changing the non-threadsafe collections to synchronized ones would > probably do the trick. In any case, I'm sure making a thread-safe > XMLClassDescriptorResolver implementation would not be that difficult > and we will be more than happy to contribute codes and run some highly > concurrent tests if the Castor developers agree that this is the best > way forward. > > My 2 cents! > > Kind regards, > > James. > > P.S. Below is a short investigation into the possible effects of using > HashMap in a multi-threaded environment in a non-thread-safe way. > > ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ > > As Adam's colleague has done, I have isolated the HashMap race condition > (in both Java 1.4 and 1.5). The problem happens with concurrent writes > to the map, no reads are necessary to recreate the problem (though they > also have a race condition when there are concurrent writes). > > Below is the source for HashMap.put: > > <code> > > public V put(K key, V value) { > if (key == null) > return putForNullKey(value); > int hash = hash(key.hashCode()); > int i = indexFor(hash, table.length); > for (Entry<K,V> e = table[i]; e != null; e = e.next) { > Object k; > if (e.hash == hash && ((k = e.key) == key || > key.equals(k))) { > V oldValue = e.value; > e.value = value; > e.recordAccess(this); > return oldValue; > } > } > > modCount++; > addEntry(hash, key, value, i); > return null; > } > > </code> > > As you can see, the for loop will only complete when "e != null" - that > is to say, that the last entry in the table (Entry[]) is null. The call > to HashMap.addEntry near the end of the method calls the HashMap.resize > if the size of the array that backs the map needs to be resized. It > seems that a circular reference inside one of the bins _can_ occur and > _might_ cause a race condition. See the code for HashMap.transfer below: > > <code> > > void transfer(Entry[] newTable) { > Entry[] src = table; > int newCapacity = newTable.length; > for (int j = 0; j < src.length; j++) { > Entry<K,V> e = src[j]; > if (e != null) { > src[j] = null; > do { > Entry<K,V> next = e.next; > int i = indexFor(e.hash, newCapacity); > e.next = newTable[i]; > newTable[i] = e; > e = next; > } while (e != null); > } > } > } > > </code> > > I am able to recreate the problem with the code below. If the HashMap is > initialised to "maxSize", I have not seen the race condition occur, > however, if it is initalised to 1 (as in the code below), the race > condition happens relatively frequently. > > <code> > > /** > * HashMapTester > * Show a race condition HashMap > */ > public class HashMapTester{ > > /** > * @param args > */ > public static void main(String[] args) { > new HashMapTester().run(); > } > > public void run() { > int max = 10; > int maxSize = 100000; > List<HashMapWriter> writers = new ArrayList<HashMapWriter>(max); > Map<String,String> m = new HashMap<String,String>(1); > for(int i = 0; i < max; i++) { > HashMapWriter writer = new HashMapWriter(m,maxSize); > writers.add(writer); > writer.start(); > System.out.println("Num writers created: " + (i + 1)); > } > } > > > class HashMapWriter extends Thread{ > Random r = new Random(System.currentTimeMillis()); > Map<String, String> m = null; > int maxSize =-1; > @Override > public void run() { > while(true) { > if(m.size() < maxSize || m.size() == 0) { > String something = String.valueOf(m.size()); > m.put(something,something); > if(m.size() % 100 == 0) { > System.out.println("Map size is now: " + > m.size()); > } > }else { > String something = > String.valueOf(r.nextInt(m.size())); > m.put(something, something); > try { > Thread.sleep(r.nextInt(10) +1 ); > } catch (InterruptedException e) { > e.printStackTrace(); > } > } > > } > } > public HashMapWriter(Map<String, String> m, int maxSize) { > this.m = m; > this.maxSize = maxSize; > } > } > } > > </code> > > --------------------------------------------------------------------- > To unsubscribe from this list please visit: > > http://xircles.codehaus.org/manage_email --------------------------------------------------------------------- To unsubscribe from this list please visit: http://xircles.codehaus.org/manage_email

