• 内存泄漏的原因及解决


    转自:https://my.oschina.net/hiease/blog/1613871

    虽然jvm有垃圾回收机制,如果程序编写不注意某些特定规则,仍然会导致java程序内存泄漏,最终可能出现OutOfMemory异常。

    1.Java内存泄漏的原因

    java中的对象从使用上分为2种类型,被引用(referenced)的和不被引用(unreferenced)的。垃圾回收只会回收不被引用的对象。被引用的对象,即使已经不再使用了,也不会被回收。因此如果程序中有大量的被引用的无用对象时,就是出现内存泄漏。

    2.java堆内存(Heap)泄漏

    jvm堆内存的大小是通过 -Xms 和 -Xmx两个参数指定的。

    2.1 对象被静态成员引用

    当大对象被静态成员引用时,会造成内存泄漏。

    示例:

    private Random random = new Random();
    
    public static final ArrayList<Double> list = new ArrayList<Double>(1000000);
    
    for (int i = 0; i < 1000000; i++) { list.add(random.nextDouble()); }

    ArrayList是在堆上动态分配的对象,正常情况下使用完毕后,会被gc回收,但是在此示例中,由于被静态成员list引用,而静态成员是不会被回收的,所以会导致这个很大的ArrayList一直停留在堆内存中。

    因此需要特别注意静态成员的使用方式,避免静态成员引用大对象或集合类型的对象(如ArrayList等)。

    2.2 String的intern方法

    在大字符串上调用String.intern() 方法,intern()会将String放在jvm的内存池中(PermGen ),而jvm的内存池是不会被gc的。因此如果大字符串调用intern()方法后,会产生大量的无法gc的内存,导致内存泄漏。

    如果必须要使用大字符串的intern方法,应该通过-XX:MaxPermSize参数调整PermGen内存的大小。

    2.3 读取流后没有关闭

    开发中经常忘记关闭流,这样会导致内存泄漏。因为每个流在操作系统层面都对应了打开的文件句柄,流没有关闭,会导致操作系统的文件句柄一直处于打开状态,而jvm会消耗内存来跟踪操作系统打开的文件句柄。 示例:

    BufferedReader br = new BufferedReader(new FileReader(path));
    return br.readLine();

    要解决这个问题,在java8之前的版本中可以在finally中加入关闭操作:

     BufferedReader br = new BufferedReader(new FileReader(path));
        try {
            return br.readLine();
        } finally {
            if (br != null) br.close();
        }

    java8中可以使用try-with-resources语句:

    try (BufferedReader br = new BufferedReader(new FileReader(path))) {
        return br.readLine();
    }

    对于网络连接和数据库连接等也要注意连接的关闭,如果采用了连接池,那关闭操作是由连接池负责的,程序中可以不用处理。

    2.4 将没有实现hashCode()和equals()方法的对象加入到HashSet中

    这是一个简单却很常见的场景。正常情况下Set会过滤重复的对象,但是如果没有hashCode() 和 equals()实现,重复对象会不断被加入到Set中,并且再也没有机会去移除。

    因此给类都加上hashCode() 和 equals()方法的实现是一个好的编程习惯。可以通过Lombok的@EqualsAndHashCode很方便实现这种功能。

    3. 查找内存泄漏的方法

    3.1 记录gc日志

    通过在jvm参数中指定-verbose:gc,可以记录每次gc的详细情况,用于分析内存的使用。

    3.2 进行profiling

    通过Visual VM或jdk自带的Java Mission Control,进行内存分析。

    3.3 代码审查

    通过代码审查和静态代码检查,发现导致内存泄漏问题的错误代码。

    4. 总结

    代码层面的检查可以帮助发现部分内存泄漏的问题,但是生产环境中的内存泄漏往往不容易提前发现,因为很多问题是在大并发场景下才会出现。因此还需要通过压力测试工具进行压力测试,提前发现潜在的内存泄漏问题。

  • 相关阅读:
    地铁图快速寻路算法
    手工下载器
    在Windows7下玩老游戏花屏的解决办法
    使用代码生成建立可扩展序列化器(上)
    用Java写成的Tiger到JVM编译器
    魔王的反击
    爬取排行榜123网站之2019年上海企业前20强
    微博热搜排行榜前十
    [翻译]Everything you know about CSS is wrong!
    YSlow 1/13 Minimize HTTP Requests
  • 原文地址:https://www.cnblogs.com/sjxbg/p/11643715.html
Copyright © 2020-2023  润新知