如何在我的Java应用程序中查找内存泄漏问题

3
这是我之前问题HERE的后续问题。我发现我的Java应用程序存在内存泄漏。最初,我认为泄漏来自于应用程序的服务器组件。但根据其他人的建议,它并不是。我使用了一个工具来转储堆内存,并使用JProfiler可视化它。显然,这是由于我怀疑的HashMaps引起的。但我不确定,因为我不熟悉如何解释这个转储文件。

enter image description here

这是我应用程序的简要结构(每15分钟缓存一些文本数据以快速检索服务器线程)。
导致泄漏问题的原因是什么?如何从以下转储中识别它? 显然,我使用 new Object() 和 HashMap.put() 的方式存在一些泄漏问题!
首先是启动类/主类。在这里,我初始化了7个主要的HashMap,每个HashMap将一个键(现在只有一个 - 最终将有16个键)映射到大约4000个单行JSON字符串的时间序列NavigableMap。
public class MyCache {
        static HashMap <String, NavigableMap <Long, String>> map1= new HashMap <String, NavigableMap <Long, String>> ();
        static HashMap <String, NavigableMap <Long, String>> map2= new HashMap <String, NavigableMap <Long, String>> ();
        static HashMap <String, NavigableMap <Long, String>> map3= new HashMap <String, NavigableMap <Long, String>> ();
        static HashMap <String, NavigableMap <Long, String>> map4= new HashMap <String, NavigableMap <Long, String>> ();
        static HashMap <String, NavigableMap <Long, String>> map5= new HashMap <String, NavigableMap <Long, String>> ();
        static HashMap <String, NavigableMap <Long, String>> map6= new HashMap <String, NavigableMap <Long, String>> ();
        static HashMap <String, NavigableMap <Long, String>> map7= new HashMap <String, NavigableMap <Long, String>> ();

        public static void main(String[] args) throws Exception {
    new Server();
    new Aggregation();
    }
}

Aggregation()中,我从HTTP资源获取一些文本,将它们转换为JSON字符串,并将它们缓存到一些临时的NavigableMaps中,然后将它们放入主要的HashMap中(这样刷新不会对服务器产生太大影响)。
public class Aggregation {
    static NavigableMap <Long, String> map1Temp= new ConcurrentSkipListMap <Long, String> ();;
    static NavigableMap <Long, String> map2Temp = new ConcurrentSkipListMap <Long, String> ();
    static NavigableMap <Long, String> map3Temp= new ConcurrentSkipListMap <Long, String> ();
    static NavigableMap <Long, String> map4Temp= new ConcurrentSkipListMap <Long, String> ();
    static NavigableMap <Long, String> map5Temp = new ConcurrentSkipListMap <Long, String> ();
    static NavigableMap <Long, String> map6Temp = new ConcurrentSkipListMap <Long, String> ();
    static NavigableMap <Long, String> map7Temp = new ConcurrentSkipListMap <Long, String> ();

public Aggregation(){

// loop to cache last 15 mins
while (true) {
            logger.info("START REFRESHING ...");
    for (int i = 0; i < mylist.size(); i++) {
        long startepoch = getTime(mylist.get(i).time);
        MyItem m = mylist.get(i);
        String index=(i+1)+"";

        process1(index, m.name, startepoch);
        //adds to map1Temp
        process2(index, m.name, startepoch);
        //adds to map2Temp
        process3(index, m.name, startepoch);
        //adds to map3Temp
        process4(index, m.name, startepoch);
        //adds to map4Temp
        process5(index, m.name, startepoch);
        //adds to map5Temp
        process6(index, m.name, startepoch);
        //adds to map6Temp
        process7(index, m.name, startepoch);
        //adds to map7Temp
        }

    //then `put` them in the main `HashMap` all at-once:
            MyCache.map1.put(channel, new ConcurrentSkipListMap <Long, String> (map1Temp));
            MyCache.map2.put(channel, new ConcurrentSkipListMap <Long, String> (map2Temp));
            MyCache.map3.put(channel, new ConcurrentSkipListMap <Long, String>(map3Temp));
            MyCache.map4.put(channel, new ConcurrentSkipListMap <Long, String>(map4Temp));
            MyCache.map5.put(channel, new ConcurrentSkipListMap <Long, String> (map5Temp));
            MyCache.map6.put(channel, new ConcurrentSkipListMap <Long, String> (map6Temp));
            MyCache.map7.put(channel, new ConcurrentSkipListMap <Long, String> (map7Temp));

//printing the size of all Hashmap entries. They don't grow :-/
logger.info("\t"+"map1.size(): "+MyCache.map1.get(key).size());
logger.info("\t"+"map2.size(): "+MyCache.map2.get(key).size());
//and other 5...        

//then clear the temp maps so they don't grow over and over
            map1Temp.clear();
            map2Temp.clear();
            map3Temp.clear();
            map4Temp.clear();
            map5Temp.clear();
            map6Temp.clear();
            map7Temp.clear();
    }
//sleep for 15 min until next caching cycle
Thread.sleep(cacheEvery*1000*60);
}
1个回答

3
内存分析器显示您有3个庞大的HashMap数据结构占用约8GB的RAM,包括它们所引用的闭包键和值对象。看起来它们可能是映射到映射的地图。
这很可能就是您内存泄漏的证据。您的应用程序正在向映射数据结构添加越来越多的条目,并且(假设)没有将它们删除。这是一种内存泄漏形式。
(请注意,这是您在之前问题中没有向我们展示的代码的一部分...)

添加了我的所有类的代码片段。我想我的 Hashmaps 不应该增长。你能看一下吗? - Tina J
1
显然,它们确实会变得很大。如果地图每15分钟被清除一次,那么这意味着约8GB是15分钟的数据。但无论如何,您应该假设分析器实际上告诉您有关这些地图内存利用情况的真相。 - Stephen C
你是如何找到那8GB的?我看不出来任何线索! - Tina J
1
我数错了,它比那个多。3,135 + 2,627 + 2,570 + 2,378 + 2,125 + ...超过10GB。现在我不是那个UI的专家,它可能会说地图之间有一些共享。但它仍然在说“很多内存”。 - Stephen C
根据我对你的代码的阅读,那可能是不正确的。你似乎正在创建tempMap<n>实例的副本,但清除原始实例。然后将这些副本放入MyCache映射中。如果channel引用更改或其指向的值是可变的,则可能会在MyCache映射中积累多个ConcurrentSkipListMap实例。事实上,这就是分析器似乎在说发生的事情。相信证据! - Stephen C
显示剩余6条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接