What are the rules? Naturally, the only definitive answer can be given can be given by the HotSpot JIT.
That's not a very satifying answer, but it's true. The server JIT, like most compilers, has been taught hundreds and hundreds simplifying transformations that, in part, enable dead code elimination. Enumerating the criteria is a nearly impossible task.
But, your specific example is an interesting case.
int[] foo = new int[1000];
int bar = foo.length;
For simplicity, I assume foo and bar are locals to a method, and in this case, the 6.0 HotSpot Server JIT will recognize that foo is unchanged before the initialization of bar, and effectively change it to:
int[] foo = new int[1000];
int bar = 1000;
The JIT can't get rid of the allocation of the new array without a more advanced optimization called Escape Analysis, which attempts to determine the dynamic scope of objects. Of course, if these two lines comprise the whole method, then the analysis is fairly trivial.
Any intervening code will make the analysis more complex:
int[] foo = new int[1000];
baz(foo);
int bar = foo.length;
qux(foo);
The calls sites will preclude your desired optimization, unless the called methods baz and qux can be inlined and analyzed. The code in baz can neither modify foo nor allow it to escape, and qux can not allow it to escape.
HotSpot's implementation of escape analysis is not quite yet complete, but should be available relatively soon.