Some people say that there are two hard problems in computer science: naming, cache invalidation, and off by one errors. There’s really three, but “data lifetime” was freed just before the statement was first published.
Lifetime is a problem that is intimately tied to ownership. If you pass a pointer to a library, do you know when it is no longer used by the library? Even the simplest cases don’t always work the way you expect. Several years ago I inherited a unit test library that only stored a pointer to error messages which made the errors, uhm, interesting if they were pushed to the output stream after a locally-created buffer went out of scope.
Like pretty much everything, concurrency complicates lifetimes. When multiple threads share data it doesn’t take much before ownership becomes hazy. “He who creates the data destroys the data” is a good rule to follow but it requires special attention if you’re doing fire-and-forget job scheduling.
The adverse effects of data lifetime have been solved. Garbage collection and reference counting (ie: garbage collection for people who think they’re above using garbage collection) prevent data from being released too early by tracking ownership and postponing the release and invalidation of that section of memory. They change the problem of lifetime a simplified case of ownership, but as long as they’re used properly you won’t inadvertantly blow your foot off.
But this isn’t all doom and gloom. Although it takes some extra planning it’s entirely possible to plan out data lifetimes in your game which can have a significant payoff. For example, complex expensive allocators can be replaced with stack allocators, or the process can be elided entirely by making storage static. And, at the very least, you’ll encounter fewer crashes and race conditions which alone is worth its weight in grey hair.