The Javadoc for WeakHashMap class explains why this would happen:
Map invariants do not hold for this class. Because the garbage
  collector may discard keys at any time, a WeakHashMap may behave as
  though an unknown thread is silently removing entries
Furthermore, the iterator generated under the hood by the enhanced for-loop you're using is of fail-fast type as per quoted explanation in that javadoc.
The iterators returned by the iterator method of the collections
  returned by all of this class's "collection view methods" are
  fail-fast: if the map is structurally modified at any time after the
  iterator is created, in any way except through the iterator's own
  remove method, the iterator will throw a
  ConcurrentModificationException. Thus, in the face of concurrent
  modification, the iterator fails quickly and cleanly, rather than
  risking arbitrary, non-deterministic behavior at an undetermined time
  in the future.
Therefore your loop can throw this exception for these reasons:
- Garbage collector has removed an object in the keyset.
- Something outside the code added an object to that map.  
- A modification occurred inside the loop.  
As your intent appears to be processing the objects that are not GC'd yet, I would suggest using an iterator as follows:
Iterator<String> it = data.keySet().iterator();
int count = 0;
int maxTries = 3;
while(true) {
    try {
        while (it.hasNext()) {
            String str = it.next();
            // do something
        }
        break;
    } catch (ConcurrentModificationException e) {
        it = data.keySet().iterator(); // get a new iterator
        if (++count == maxTries) throw e;
    }
}