How does memory fragmentation occur in dynamic memory allocation, and how do you avoid it in resource-constrained environments?