The 2 comes from the fact that "x*" can match nothing. For "xxxx", "x*" matches the null width "beginning" then matches all the "x"s to the end.
The 9 is because of the non-greediness, so you get a match at the null-width beginning, at each character, between each character, and the end.
: I ran your original code using "use re 'debug'" and it looks like when you get 2 it is actually matching the "xxxx" and then the end not the beginning.