On the initial Region.put(ObjA) and/or Region.put(ObjX) calls (providing your application is a "peer cache", not a client with a PROXY or CACHING_PROXY Region), there would only be 1 instance of ObjC, referred to indirectly by ObjA (through ObjB) and ObjX (through ObjY).
However, when GemFire "replicates" the ObjA and ObjX through distribution (as would be the case for either a REPLICATE or PARTITION Region, strictly speaking Server-side here), then ObjC would be "duplicated" on the receiving side.
This is mainly due to the fact that ObjA/ObjX must be "serialized" when sent over the wire during replication and distribution to other members in the cluster that also host the same Region. The same is also true between client/server interactions (namely with PROXY and CACHING_PROXY client Regions).
The only time this would not be the case is when the Region is local only and does not distribute it's data or events (it can still be configured to receive data events though).
You can control the "de/serialization" mechanism by referring to GemFire's UG here...
http://gemfire.docs.pivotal.io/latest/userguide/developing/data_serialization/chapter_overview.html
Specifically, you will get the most bang for you buck by using GemFire's own serialization strategy with PDX (http://gemfire.docs.pivotal.io/latest/userguide/developing/data_serialization/gemfire_pdx_serialization.html), and perhaps implementing your own PdxSerializer (http://gemfire.docs.pivotal.io/latest/userguide/developing/data_serialization/use_pdx_serializer.html) or having your domain objects implement PdxSerializable (http://gemfire.docs.pivotal.io/latest/userguide/developing/data_serialization/use_pdx_serializable.html), though a bit more invasive.