Maybe it is just an impression, but I have memories of similar situations, as far as my first Minecraft single player world a few years ago and more recently in multiplayer.
I arrived in some plain biome that was filled with sheeps, cows, maybe pigs, maybe a few chicken and horse or donkey here and there.
I have the impression that after some time building a house, all these animal have a tendancy to gather closer that to the neighbour forest, while in the past they use to hang around in the middle of the plain. There also seems to be less of them?
So maybe it's just my imagination but:
Do animals have a tendency to get slightly away from play builds? (Like player placed blocks?)
I think I also read once that if it was possible to a mob to walk 128 blocks away, they it potentially could despawn ... and maybe, due to the chunks being loaded, some animals despawn ? On the other hand I thought friendly mobs didn't despawn?
In multiplayer, this could also have to do with friendly mob cap (in addition to chunk being loaded) ?
Passive mobs (animals) never despawn with the exception of untamed ocelots, which for whatever reason spawn under the hostile mob cap but count as passive mobs (if they didn't despawn an infinite number could accumulate). However, there are bugs which cause mobs of all sorts to glitch into walls and suffocate (it is particularly unfortunate that these bugs still exist since there are verified fixes posted to Mojang's own bug tracker). They can also wander into holes and caves and get stuck or die (I've seen animals fall and die in front of me while exploring ravines; they won't jump down on their own but other mobs can push them in. In another case, I found several sheep in a cave at lava level, which may have been washed down by water since they were all in the same area).
Otherwise, passive mobs are attracted to grass blocks (they will favor them over other blocks when randomly wandering around), which is the only reason I can think of for them moving away from builds; light also attracts them but not as much as grass. There is also no way for the game to know if a block is player-placed as there is no such tag (or the capability for one, only items can have the necessary NBT data). Also, various mobs will attack other mobs, such as wolves and sheep, so the prey animals will gradually be wiped out, which requires that the player be nearby (the random wander AI only runs when a player is within 32 blocks; other AI functions, including attacking, can run when they are further away but they need to be close enough to see a target, just as most hostile mobs need to be within 16 blocks of a player to target them).
The ocelot is under the hostile mob category because the source code has It hostile towards Chickens, Rabbits and Baby Sea Turtles.
True hostile mobs are hostile towards the player, and internally they are instances of "IMob", which ocelots are not, they are instances of "EntityAnimal"; you'd have to claim that wolves are also hostile but they certainly are not, even if they do attack the player (the Wiki may call such mobs "neutral" but no such category exists within the code, this is only based on their behavior):
// These are all "animals", aka passive mobs
public class EntityCow extends EntityAnimal
public class EntityOcelot extends EntityTameable
public class EntityWolf extends EntityTameable
// All passive mobs, tamable or otherwise, extend EntityAnimal
public abstract class EntityTameable extends EntityAnimal implements EntityOwnable
// These are all "monsters", aka hostile mobs (the Wiki calls Endermen "neutral" but no such category
// actually exists:
public class EntityEnderman extends EntityMob
public class EntityZombie extends EntityMob
public class EntitySlime extends EntityLiving implements IMob
// All hostile mobs implement IMob, but not all extend EntityMob (slimes do not prevent players from sleeping
// because the bed code checks if they are in instance of EntityMob, which slimes do not extend):
public abstract class EntityMob extends EntityCreature implements IMob
// These are the different types of mobs, grouped by the base class they extend/implement, which is used when
// counting entities towards the mob cap:
monster(IMob.class, 70, Material.air, false, false),
creature(EntityAnimal.class, 10, Material.air, true, true),
ambient(EntityAmbientCreature.class, 15, Material.air, true, false),
waterCreature(EntityWaterMob.class, 5, Material.water, true, false);
The game also doesn't care which spawn list a mob in placed in, except that they should be within the correct category so they count towards that mob cap (ocelots count towards the passive mob cap, and as a result I've seen hundreds spawning in jungles after I lit up all the caves (note the "E:" number), leaving little room for hostile mobs to spawn. This led me to create a separate mob cap for ocelots, set to 25). You'd have to ask Mojang why they coded ocelots the way they did, I'd personally have placed them in the passive mob spawn list (and actually have, in addition to the hostile list; the passive mob list also spawns mobs during world generation and these ocelots never despawn, in addition to "hostile" ocelots).
Of course, the game makes no distinction between the behavior of a mob based on its "category"; you can add a "hostile" mob which is entirely passive and vice-versa, then there are mobs with both behaviors (e.g. rabbits and killer rabbits; unlike "neutral" mobs, their behavior is either completely passive or hostile depending on the variant). Also, even completely passive mobs will respond to attacks by fleeing, much as "neutral" mobs will attack the player, just a different action.
Also, this is an example of what can happen when you have a mob that never despawns spawning under a different mob cap; parrots initially had the same spawn rules as ocelots (and are passive by any measure, so this had to have been done so they could spawn in existing jungles without wiping out all passive mobs; however, ocelots were added at the same time as jungles so this spawn mechanic doesn't make sense. Based on source code for 1.12.2 this is no longer the case, but ocelots are still on the hostile list):