I've spotted some race conditions in MarshalledValue; all quite unlikely still I have some suggested improvements:
- serialize() is synchronized but other methods relying on the value of "raw" are not, and serialize is modifying the value of raw[] with System.arraycopy which is not an atomic operation.
- equals() implementation is relying on the fact that instance values don't change while it's figuring out the best strategy to perform the comparison (race condition between checking for the need to serialize or deserialize and actually using the value)
- same race condition in hashCode() as well
- some volatile fields are read and written multiple times in the same method, I'll make local copies instead
- equalityPreferenceForInstance is applied inconsistently: in first part of equals implementation only the this.equalityPreferenceForInstance is considered, while in case we need to do some serialization/deserialization then the preference is decided by considering the equalityPreferenceForInstance variable of both values in &&.
- compact() is missing some lock/synch, it a very unlikely race condition is possible in which the data is completely lost (nulling out both raw and instance valueholders)
- System.arraycopy makes a copy of the buffer used by the marshaller, but that's not needed as defensive copy because the ExposedByteArrayOutputStream is discarded: don't copy it if there's no need to resize the buffer.