自己生成一个内存泄漏的代码

2018-01-17 09:40:41

内存泄漏一般都是无意之间产生的。可是有人让你估计产生内存泄漏,你该怎么做呢?这样的事情并不奇葩,我在面试中就遇到过。当时回答的不是很完美,现在查了相关资料,在这里做个总结!

那么怎么才能产生一个内存泄露呢?

解决方案且听我细细道来。在纯Java中,有一个很好的方式可以产生真正的内存泄露(通过执行代码使对象不可访问但仍存在于内存中):

  1. 应用产生一个长时间运行的线程(或者使用一个线程池加速泄露)。

  2. 线程通过一个(可选的自定义)类加载器加载一个类。

  3. 该类分配大内存(例如,new byte[1000000]),赋值给一个强引用存储在静态字段中,再将它自身的引用存储到ThreadLocal中。分配额外的内存是可选的(泄露类实例就够了),但是这样将加速泄露工作。

  4. 线程清除所有自定义类的或者类加载器载入的引用。

  5. 重复上面步骤。

HashMap 内存泄漏

下面通过一个HashMap来制造一个内存泄漏的应用。HashMap中,如果Key类没有重写HashCode和equals方法,直接保存在HashMap中,那么就会很容易造成内存泄漏。首先我们创建一个静态内部类Key类,这个内部类没有重写HashCode和equals方法,在main方法中,我们定义一个HashMap,无限循环往这个map里加入Key对象,要不了多久就会抛出内存泄漏的异常。


这个我写了无限循环,内存增长并不明显。


Vector 内存泄漏

现在我们使用 Vector 来制造一个内存泄漏的场景。看如下代码:

1
2
3
4
5
6
Vector v=new Vector(10);
for (int i=1;i<100; i++){
    Object o=new Object();
    v.add(o);
    o=null;
}

在这个例子中,代码栈中存在Vector对象的引用v和Object对象的引用o。在For循环中,我们不断的生成新的对象,然后将其添加到Vector对象中,之后将o引用置空。问题是当o引用被置空后,如果发生GC,我们创建的Object对象是否能够被GC回收呢?答案是否定的。因为,GC在跟踪代码栈中的引用时,会发现v引用,而继续往下跟踪,就会发现v引用指向的内存空间中又存在指向Object对象的引用。也就是说尽管o引用已经被置空,但是Object对象仍然存在其他的引用,是可以被访问到的,所以GC无法将其释放掉。如果在此循环之后,Object对象对程序已经没有任何作用,那么我们就认为此Java程序发生了内存泄漏。

尽管对于C/C++中的内存泄露情况来说,Java内存泄露导致的破坏性小,除了少数情况会出现程序崩溃的情况外,大多数情况下程序仍然能正常运行。但是,在移动设备对于内存和CPU都有较严格的限制的情况下,Java的内存溢出会导致程序效率低下、占用大量不需要的内存等问题。这将导致整个机器性能变差,严重的也会引起抛出OutOfMemoryError,导致程序崩溃。


代码示例如下

import java.util.HashMap;
import java.util.Vector;

public class neicunxielou {
    public static void main(String[] args) {
        HashMap demo = new HashMap();
        Vector v = new Vector(10);
        while (true) {
            Object o = new Object();
            v.add(o);
            o = null;
        }
    }
}


结果电脑内存蹭蹭的往上涨啊。涨到顶端一段时间后,出现了崩溃,程序结束。


崩溃日志如下。

"C:\Program Files\Java\jdk1.8.0_151\bin\java" "-javaagent:C:\Program Files\JetBrains\IntelliJ IDEA 2017.2.5\lib\idea_rt.jar=63224:C:\Program Files\JetBrains\IntelliJ IDEA 2017.2.5\bin" -Dfile.encoding=UTF-8 -classpath "C:\Program Files\Java\jdk1.8.0_151\jre\lib\charsets.jar;C:\Program Files\Java\jdk1.8.0_151\jre\lib\deploy.jar;C:\Program Files\Java\jdk1.8.0_151\jre\lib\ext\access-bridge-64.jar;C:\Program Files\Java\jdk1.8.0_151\jre\lib\ext\cldrdata.jar;C:\Program Files\Java\jdk1.8.0_151\jre\lib\ext\dnsns.jar;C:\Program Files\Java\jdk1.8.0_151\jre\lib\ext\jaccess.jar;C:\Program Files\Java\jdk1.8.0_151\jre\lib\ext\jfxrt.jar;C:\Program Files\Java\jdk1.8.0_151\jre\lib\ext\localedata.jar;C:\Program Files\Java\jdk1.8.0_151\jre\lib\ext\nashorn.jar;C:\Program Files\Java\jdk1.8.0_151\jre\lib\ext\sunec.jar;C:\Program Files\Java\jdk1.8.0_151\jre\lib\ext\sunjce_provider.jar;C:\Program Files\Java\jdk1.8.0_151\jre\lib\ext\sunmscapi.jar;C:\Program Files\Java\jdk1.8.0_151\jre\lib\ext\sunpkcs11.jar;C:\Program Files\Java\jdk1.8.0_151\jre\lib\ext\zipfs.jar;C:\Program Files\Java\jdk1.8.0_151\jre\lib\javaws.jar;C:\Program Files\Java\jdk1.8.0_151\jre\lib\jce.jar;C:\Program Files\Java\jdk1.8.0_151\jre\lib\jfr.jar;C:\Program Files\Java\jdk1.8.0_151\jre\lib\jfxswt.jar;C:\Program Files\Java\jdk1.8.0_151\jre\lib\jsse.jar;C:\Program Files\Java\jdk1.8.0_151\jre\lib\management-agent.jar;C:\Program Files\Java\jdk1.8.0_151\jre\lib\plugin.jar;C:\Program Files\Java\jdk1.8.0_151\jre\lib\resources.jar;C:\Program Files\Java\jdk1.8.0_151\jre\lib\rt.jar;C:\work\demo\target\classes" neicunxielou
Exception in thread "main" java.lang.OutOfMemoryError: Java heap space
	at java.util.Arrays.copyOf(Arrays.java:3210)
	at java.util.Arrays.copyOf(Arrays.java:3181)
	at java.util.Vector.grow(Vector.java:266)
	at java.util.Vector.ensureCapacityHelper(Vector.java:246)
	at java.util.Vector.add(Vector.java:782)
	at neicunxielou.main(neicunxielou.java:12)

Process finished with exit code 1



  • 2017-04-02 00:42:51

    PHP的pm、pm.max_requests、memory_limit参数优化说明

    pm是来控制php-fpm的工作进程数到底是一次性产生固定不变(static)还是在运行过程中随着需要动态变化(dynamic)。众所周知,工作进程数与服务器性能息息相关,太少则不能及时处理请求,太多则会占用内存过大而拖慢系统。

  • 2017-04-02 00:44:46

    NGINX + PHP-FPM 502 相关事

    NGINX + PHP-FPM 报 502 错误,我想大部分 SA 都遇到过吧。 根据报错的频率,可以分为两种情况,间歇性的502和连续性的502。 这里只讨论第一种情况——间歇性的502。

  • 2017-04-02 00:52:26

    php-fpm占用系统资源分析

    由上图分析,可以看出共有602个进程,其中有601个进程休眠了。这好像有点不对劲,内核进程也就80个左右,加上memcached, nginx, mysqld,也不会超出90个。除了这些,剩下的只有php-fpm管理的php-cgi,难道是…?

  • 2017-04-02 00:56:36

    php-fpm占用系统资源分析

    由上图分析,可以看出共有602个进程,其中有601个进程休眠了。这好像有点不对劲,内核进程也就80个左右,加上memcached, nginx, mysqld,也不会超出90个。除了这些,剩下的只有php-fpm管理的php-cgi,难道是…?

  • 2017-04-03 14:23:17

    Android Studio --“Cannot resolve symbol” 解决办法

    Android Studio 无法识别同一个 package 里的其他类,将其显示为红色,但是 compile 没有问题。鼠标放上去后显示 “Cannot resolve symbol XXX”,重启 Android Studio,重新 sync gradle,Clean build 都没有用。

  • 2017-04-06 14:59:13

    PHP配置文件详解

    PHP是一个简单易学,功能强大的语言,尤其在Web开发,开发效率高,方便快捷。研究一下php.ini了解PHP相关配置会有好处的,对PHP有更加深入的了解。

  • 2017-04-06 15:00:46

    怎么实时查看mysql当前连接数

    今天有一台mysql服务器突然连接数暴增,并且等待进程全部被锁...因为问题解决不当,导致被骂...OTL 总结:以后要快速定位错误,布置解决方案

  • 2017-04-06 15:07:57

    PHP-FPM不完全指南

    fpm启动后会先读php.ini,然后再读相应的conf配置文件,conf配置可以覆盖php.ini的配置。 启动fpm之后,会创建一个master进程,监听9000端口(可配置),master进程又会根据fpm.conf/www.conf去创建若干子进程,子进程用于处理实际的业务。