I found the source of the leak. It turned out that Devel::Leak::Object was useful, if I had only taken one more step. With a small set of sample data, I ran the script and then used Data::Dumper to dump out the IO object that the author assured me didn't leak. Well, as it turns out, it was caching every line of the file that it saw, plus a fair amount of derived data that it calculates for each line. The somewhat annoying thing is that it doesn't actually use 99% of that cached data for anything (one item per line does need cached). That IO object has been fixed. While it will still use some memory, it will significantly raise the that amount of data that can be processed before problems are encountered.
Thanks to all of you for your suggestions,