zoukankan      html  css  js  c++  java
  • Java内存泄漏

    Java内存泄漏

    解释

    内存泄漏: 存在一些被分配的对象,满足两个特点:

    • 对象是可达的:在有向图中,存在通路与之相连.
    • 对象是无用的:程序以后不再使用这些对象.

    结果:消耗越来越多的内存资源,最终导致OutOfMemoryError

    与C++的区别:

    • C++:对象被分配内存空间,却不可达.
    • Java:不可达的对象由GC回收.

    示例:

    Vector v = new Vector(10);
    for(int i = 0; i < 10;i++){
        Object o = new Object();
        v.add(o);
        o = null;
    }
    

    对象可以分为两种:有引用的对象无引用的对象
    其中无引用的对象由GC进行回收。
    有引用的对象不会被GC回收,即使这些对象不会再被使用。

    内存泄漏示例


    Java堆泄漏

    注:通过-Xms<size>-Xmx<size>设置堆的最小值和最大值,使得容易的重现堆泄漏。

    几种常见的泄漏情景:

    存储对象引用的静态域

    示例:

    // JVM参数设定:-Xms10m -Xmx10m
    // 会出现OutOfMemoryError
    private Random random = new Random();
    public static final ArrayList<Double> list = new ArrayList<>(1000000);
    @Test
    public void test() throws InterruptedException {
        for (int i = 0; i < 1000000; i++) { // 向static域加入对象
            list.add(random.nextDouble());
        }
    
        System.gc();
        Thread.sleep(10000); // 使得GC得以执行
    }
    

    使用非static域存放对象:

    // 不会出现OutOfMemoryError
        @Test
        public void test() throws InterruptedException {
            addelement();
            System.gc();
            Thread.sleep(10000);
        }
    
        private void addelement(){
            ArrayList<Double> list = new ArrayList<>(1000000); // 使用非static域存储对象
            for (int i = 0; i < 1000000; i++) {
                list.add(random.nextDouble());
            }
        }
    

    解决对策

    • 谨慎使用static变量,其生命周期与JVM的生命周期相同。
    • 谨慎使用集合类,其常常存储大量不会再被使用到的对象引用。

    对长字符串使用String.intern()

    示例:

    @Test
    public void test() throws InterruptedException, FileNotFoundException {
        Thread.sleep(15000);
        String str = new Scanner(new File("scr/test.txt"),"UTF-8").useDelimiter("\A").next();
        str.intern(); // 向字符串常量池中加入长字符串
    
        System.gc();
        Thread.sleep(16000);
    }
    

    解决对策

    • String.intern()方法将字符串存储在永久代空间中,若需要将大量长字符串存入字符串常量池,可通过最大永久代大小来避免异常:-XX:MaxPermSize=<size>
    • 使用Java 8:其中永久代被元空间取代,使用String.intern()不会导致异常。

    未关闭的流

    // 流在使用后没有被关闭
    @Test
    public void test() throws IOException {
        String str = "";
        URLConnection conn = new URL("http://norvig.com/big.txt").openConnection();
        BufferedReader br = new BufferedReader(new InputStreamReader(conn.getInputStream(), StandardCharsets.UTF_8));
        while (br.readLine() != null)
            str += br.readLine();
    }
    

    注:未关闭的流会导致两种类型的泄漏

    • 底层资源泄漏:文件描述符(file descriptors),打开的连接(open connections)。
    • JVM内存泄漏

    解决对策

    • 使用完流后手动关闭。
    • 使用Java 8中的自动关闭特性。
    // 无需在finally中手动关闭
    try (BufferedReader br = new BufferedReader(
      new InputStreamReader(conn.getInputStream(), StandardCharsets.UTF_8))) {
        // ...
    } catch (IOException e) {
        e.printStackTrace();
    }
    

    未关闭的连接

    与未关闭的流类似。
    一般是连接数据库或FTP后未关闭。

    // 连接FTP后未关闭
    @Test
    public void test() throws IOException {
        URL url = new URL("ftp://speedtest.tele2.net");
        URLConnection urlc = url.openConnection();
        InputStream is = urlc.getInputStream();
        String str = " ";
    }
    

    解决对策

    • 使用完连接后手动将连接关闭。

    将没有hashCode()equal()的对象加入到Hashset

    当一个对象没有重写hashCode()equals()方法时,在向HashSet集合类中添加重复的对象时,其不会忽略到重复的对象。

    @Test
    public void test(){
        Map<Object, Object> map = System.getProperties();
        while (true){
            map.put(new newObject("key"),"value");
        }
    }
    
    // 没有重写equals和hashCode的自定义类
    class newObject{
        private String key;
        public newObject(String key){
            this.key = key;
        }
    }
    

    解决方法

    • 重写equals()hashCode()方法。
    • 或者使用插件lombok

    查找泄漏源

    • 打开垃圾收集器的详细日志信息。JVM参数中添加-verbose:gc
    • 使用监控工具(Visual VM)。
    • 检查代码。

    参考:

  • 相关阅读:
    Delphi 正则表达式之TPerlRegEx 类的属性与方法(3): Start、Stop
    Delphi 正则表达式之TPerlRegEx 类的属性与方法(2): 关于子表达式
    Delphi 正则表达式之TPerlRegEx 类的属性与方法(1): 查找
    Delphi 正则表达式语法(10): 选项
    Delphi 正则表达式语法(9): 临界匹配
    Delphi 正则表达式语法(8): 引用子表达式
    Delphi 正则表达式语法(7): 匹配转义字符
    看阿里P7讲MyBatis:从MyBatis的理解以及配置和实现全帮你搞懂
    公司新来的小姐姐不懂java中的static关键字,这样给她描述不香吗?
    2020阿里,字节跳动,JAVA岗(一线企业校招、社招)面试题合集
  • 原文地址:https://www.cnblogs.com/truestoriesavici01/p/13217318.html
Copyright © 2011-2022 走看看