Проблема, я думаю, в том, что даже с АОП вы не можете перехватить процесс исключения. С помощью AOP вы можете захватить, скажем, все созданные исключения, но не можете знать, когда они потребляются.
Например:
try {
...
} catch (Exception e) {
log.boom("Ouchies happened here", e);
}
Не каждое исключение перебрасывается.
Однако на определенном уровне МОСТ исключение, в конце концов, генерируется, чтобы вызывающий мог обработать их.
Итак, учитывая, что в любом случае вы, скорее всего, «утечете» исключения, игра должна попытаться найти «протекающие» исключения.
Используя AOP, вы можете скрывать каждое Исключение по мере его создания (можете ли вы сделать это на уровне Exception или делать это на уровне отдельного класса, я не могу сказать - не слишком знакомо с AOP в практика в этом случае).
Как только вы захватываете исключения по мере их создания, вы начинаете переносить вызовы методов через AOP. Когда вы сделаете это, вы можете перехватывать исключения, создаваемые методом.
Итак, немного поработав, вы узнаете, возвращаясь из метода а) какие исключения были созданы и б) какие были выброшены. Если вы пройдете исключение, «вызванное» деревом возвращенного исключения из метода, вы можете выбросить их из списка «исключений, созданных этим методом». Остальные утечки.
В идеале, после небольшого анализа, вы сможете определить, какие из них просто не выброшены (но обрабатываются каким-либо другим способом), а какие на самом деле затенены вашими блоками finally.
Это несовершенно, и, безусловно, я бы не стал использовать его в производстве (мне просто нужно было бы приложить немало усилий, чтобы разобраться во всех условиях гонки, связанных с записью и тому подобным. Больше работы, чем я хотел бы сделать для такого рода отладки, честно говоря). Но он может дать вам информацию, которую вы ищете, особенно если использовать ее в небольшом домене классов.