如何用Java编写一段代码引发内存泄露代码

如何用Java编写一段代码引发内存泄露_百度知道怎样用Java编写一段代码引发内存泄露 - 推酷
怎样用Java编写一段代码引发内存泄露
通过下面步骤能够非常easy产生内存泄露(程序代码不能訪问到某些对象,可是它们仍然保存在内存中):
应用程序创建一个长时间执行的线程(或者使用线程池,会更快地发生内存泄露)。
线程通过某个类载入器(能够自己定义)载入一个类。
该类分配了大块内存(比方new byte[1000000]),在某个静态变量存储一个强引用,然后在ThreadLocal中存储它自身的引用。分配额外的内存new byte[1000000]是可选的(类实例泄露已经足够了),可是这样会使内存泄露更快。
线程清理自己定义的类或者载入该类的类载入器。
反复以上步骤。
因为没有了对类和类载入器的引用,ThreadLocal中的存储就不能被訪问到。ThreadLocal持有该对象的引用,它也就持有了这个类及其类载入器的引用,类载入器持有它所载入的类的全部引用,这样GC无法回收ThreadLocal中存储的内存。在非常多JVM的实现中Java类和类载入器直接分配到permgen区域不运行GC,这样导致了更严重的内存泄露。
这样的泄露模式的变种之中的一个就是假设你常常又一次部署以不论什么形式使用了ThreadLocal的应用程序、应用容器(比方Tomcat)会非常easy发生内存泄露(因为应用容器使用了如前所述的线程,每次又一次部署应用时将使用新的类载入器)。
静态变量引用对象
MemorableClass {
ArrayList list =
ArrayList(
调用长字符串的
String.intern()
String str=readString();
// read lengthy string any source db,textbox/jsp etc..
str.intern();
未关闭已打开流(文件,网络等)
BufferedReader br =
BufferedReader(
FileReader(inputFile));
(Exception e) {
e.printStacktrace();
未关闭连接
Connection conn = ConnectionFactory.getConnection();
(Exception e) {
e.printStacktrace();
JVM的GC不可达区域
比方通过native方法分配的内存。
web应用在application范围的对象,应用未重新启动或者没有显式移除
getServletContext().setAttribute(&SOME_MAP&, map);
web应用在session范围的对象,未失效或者没有显式移除
session.setAttribute(&SOME_MAP&, map);
不对或者不合适的JVM选项
比方IBM JDK的noclassgc阻止了无用类的垃圾回收
假设HashSet未正确实现(或者未实现)hashCode()或者equals(),会导致集合中持续添加?“副本”。假设集合不能地忽略掉它应该忽略的元素,它的大小就仅仅能持续增长,并且不能删除这些元素。
假设你想要生成错误的键值对,能够像以下这样做:
// no hashCode or equals();
BadKey(String key) {
Map map = System.getProperties();
// Memory leak even if your threads die.
除了被遗忘的监听器,静态引用,hashmap中key错误/被改动或者线程堵塞不能结束生命周期等典型内存泄露场景,以下介绍一些不太明显的Java发生内存泄露的情况,主要是线程相关的。
Runtime.addShutdownHook后没有移除,即使使用了removeShutdownHook,由于ThreadGroup类对于未启动线程的bug,它可能不被回收,导致ThreadGroup发生内存泄露。
创建但未启动线程,与上面的情形同样
创建继承了ContextClassLoader和AccessControlContext的线程,ThreadGroup和InheritedThreadLocal的使用,全部这些引用都是潜在的泄露,以及全部被类载入器载入的类和全部静态引用等等。这对ThreadFactory接口作为重要组成元素整个j.u.c.Executor框架(java.util.concurrent)的影响非常明显,非常多开发者没有注意到它潜在的危急。并且非常多库都会依照请求启动线程。
ThreadLocal缓存,非常多情况下不是好的做法。有非常多基于ThreadLocal的简单缓存的实现,可是假设线程在它的期望生命周期外继续执行ContextClassLoader将发生泄露。除非真正必要不要使用ThreadLocal缓存。
当ThreadGroup自身没有线程可是仍然有子线程组时调用ThreadGroup.destroy()。发生内存泄露将导致该线程组不能从它的父线程组移除,不能枚举子线程组。
使用WeakHashMap,value直接(间接)引用key,这是个非常难发现的情形。这也适用于继承Weak/SoftReference的类可能持有对被保护对象的强引用。
使用http(s)协议的java.net.URL下载资源。KeepAliveCache在系统ThreadGroup创建新线程,导致当前线程的上下文类载入器内存泄露。没有存活线程时线程在第一次请求时创建,所以非常有可能发生泄露。
(在Java7中已经修正了,创建线程的代码合理地移除了上下文类载入器。)
使用InflaterInputStream在构造函数(比方PNGImageDecoder)中传递new java.util.zip.Inflater(),不调用inflater的end()。不过new的话非常安全,但假设自己创建该类作为构造函数參数时调用流的close()不能关闭inflater,可能发生内存泄露。这并非真正的内存泄露由于它会被finalizer释放。但这消耗了非常多native内存,导致linux的oom_killer杀掉进程。所以这给我们的教训是:尽可能早地释放native资源。
java.util.zip.Deflater也一样,它的情况更加严重。好的地方可能是非常少用到Deflater。假设自己创建了Deflater或者Inflater记住必须调用end()。
已发表评论数()
请填写推刊名
描述不能大于100个字符!
权限设置: 公开
仅自己可见
正文不准确
标题不准确
排版有问题
主题不准确
没有分页内容
图片无法显示
视频无法显示
与原文不一致&&国之画&&&&&&
版权所有 京ICP备号-2
迷上了代码!java 为什么会内存泄露,这个典型的例子为什么会造成内存泄露?
[问题点数:40分,结帖人xooxoo]
java 为什么会内存泄露,这个典型的例子为什么会造成内存泄露?
[问题点数:40分,结帖人xooxoo]
不显示删除回复
显示所有回复
显示星级回复
显示得分回复
只显示楼主
2008年10月 Java大版内专家分月排行榜第三
2008年10月 Java大版内专家分月排行榜第三
2008年10月 Java大版内专家分月排行榜第三
2011年4月 Java大版内专家分月排行榜第二2010年8月 Java大版内专家分月排行榜第二2010年5月 Java大版内专家分月排行榜第二2008年2月 Java大版内专家分月排行榜第二2007年7月 Java大版内专家分月排行榜第二
2011年2月 Java大版内专家分月排行榜第三2010年9月 Java大版内专家分月排行榜第三2008年9月 Java大版内专家分月排行榜第三2008年1月 Java大版内专家分月排行榜第三2007年11月 Java大版内专家分月排行榜第三2007年9月 Java大版内专家分月排行榜第三
2011年4月 Java大版内专家分月排行榜第二2010年8月 Java大版内专家分月排行榜第二2010年5月 Java大版内专家分月排行榜第二2008年2月 Java大版内专家分月排行榜第二2007年7月 Java大版内专家分月排行榜第二
2011年2月 Java大版内专家分月排行榜第三2010年9月 Java大版内专家分月排行榜第三2008年9月 Java大版内专家分月排行榜第三2008年1月 Java大版内专家分月排行榜第三2007年11月 Java大版内专家分月排行榜第三2007年9月 Java大版内专家分月排行榜第三
2011年4月 Java大版内专家分月排行榜第二2010年8月 Java大版内专家分月排行榜第二2010年5月 Java大版内专家分月排行榜第二2008年2月 Java大版内专家分月排行榜第二2007年7月 Java大版内专家分月排行榜第二
2011年2月 Java大版内专家分月排行榜第三2010年9月 Java大版内专家分月排行榜第三2008年9月 Java大版内专家分月排行榜第三2008年1月 Java大版内专家分月排行榜第三2007年11月 Java大版内专家分月排行榜第三2007年9月 Java大版内专家分月排行榜第三
2011年4月 Java大版内专家分月排行榜第二2010年8月 Java大版内专家分月排行榜第二2010年5月 Java大版内专家分月排行榜第二2008年2月 Java大版内专家分月排行榜第二2007年7月 Java大版内专家分月排行榜第二
2011年2月 Java大版内专家分月排行榜第三2010年9月 Java大版内专家分月排行榜第三2008年9月 Java大版内专家分月排行榜第三2008年1月 Java大版内专家分月排行榜第三2007年11月 Java大版内专家分月排行榜第三2007年9月 Java大版内专家分月排行榜第三
本帖子已过去太久远了,不再提供回复功能。探究内存泄露—Part1—编写泄露代码 - ImportNew
几天前我发现了一个小问题:有一个服务器在跑了一段时间后挂掉了。重启脚本和系统后,这个问题还是会出现。因为问题代码不是关键业务,所以尽管有大量的数据丢失,但是问题并不严重。不过我还是决定作进一步的调查,来探寻一下问题到底出现在哪。首先注意到的是,服务器通过了所有的单元测试和集成环境的完整测试。在测试环境下使用测试数据时运行得非常正常。那么为什么在工作环境中一跑起来就会出现问题呢?很容易就能想到,也许是因为在实际运行时的负载大于测试,甚至超过了设计时所能承载的负重,从而耗尽了资源。但是到底是什么资源,又是在哪里耗尽的呢?这就是本文需要探究的难题。
为了演示如何调查这个问题,第一件事情就是写一些内存泄露的代码。我将会采用生产者—消费者模型,以便更好的说明这个问题。
和往常一样,为了说明内存泄露代码,我需要人为建立一个场景。在这个场景中,假定你为一个证劵经纪公司工作,这个公司将股票的销售额和股份记录在一个数据库中。通过一个简单进程获取命令并将其存放在一个队列中。另一个进程从该队列中读取命令并将其写入数据库。命令的POJO(简单Java对象)非常的直观:
public class Order {
private final S
private final long[]
* @param id
The order id
* @param code
The stock code
* @param amount
the number of shares
* @param price
the price of the share
* @param time
the transaction time
public Order(int id, String code, int amount, double price, long time) {
this.code =
this.amount =
this.price =
this.time =
// This just makes the Order object bigger so that
// the example runs out of heap more quickly.
this.padding = new long[3000];
Arrays.fill(padding, 0, padding.length - 1, -2);
public int getId() {
public String getCode() {
public int getAmount() {
public double getPrice() {
public long getTime() {
这个命令POJO是Spring应用的一部分。这个应用有三个主要的抽象类,当应用调用他们的start()方法时分别创建一个新进程。
第一个抽象类是OrderFeed。run()方法会生成一个虚拟的命令并将其放置在队列中。生成命令后它会睡眠一会儿,然后生成一个新的命令。
public class OrderFeed implements Runnable {
private static Random rand = new Random();
private static int id = 0;
private final BlockingQueue&Order& orderQ
public OrderFeed(BlockingQueue&Order& orderQueue) {
this.orderQueue = orderQ
* Called by Spring after loading the context. Start producing orders
public void start() {
Thread thread = new Thread(this, &Order producer&);
thread.start();
/** The main run loop */
public void run() {
while (true) {
Order order = createOrder();
orderQueue.add(order);
private Order createOrder() {
final String[] stocks = { &BLND.L&, &DGE.L&, &MKS.L&, &PSON.L&, &RIO.L&, &PRU.L&,
&LSE.L&, &WMH.L& };
int next = rand.nextInt(stocks.length);
long now = System.currentTimeMillis();
Order order = new Order(++id, stocks[next], next * 100, next * 10, now);
private void sleep() {
TimeUnit.MILLISECONDS.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
第二个类是OrderRecord,这个类负责从队列中提取命令并将它们写入数据库。问题是,将命令写入数据库的耗时比产生命令的耗时要长得多。为展示这一现象,我将在recordOrder()方法中让其睡眠1秒。
public class OrderRecord implements Runnable {
private final BlockingQueue&Order& orderQ
public OrderRecord(BlockingQueue&Order& orderQueue) {
this.orderQueue = orderQ
public void start() {
Thread thread = new Thread(this, &Order Recorder&);
thread.start();
public void run() {
while (true) {
Order order = orderQueue.take();
recordOrder(order);
} catch (InterruptedException e) {
e.printStackTrace();
* Record the order in the database
* This is a dummy method
* @param order
* @throws InterruptedException
public void recordOrder(Order order) throws InterruptedException {
TimeUnit.SECONDS.sleep(1);
结果将是显而易见的,OrderRecord线程跟不上命令产生的速度,导致这个队列越来越长,直到JAVA虚拟机用尽堆内存从而崩溃。这就是生产者—消费者模式的存在一个大问题:消费者的速度必须跟上生产者的速度。
为了证明这一点,我加入了第三个类OrderMonitor。这个类每隔几秒就会打印出队列的大小,这样就能看到运行时产生的问题。
public class OrderQueueMonitor implements Runnable {
private final BlockingQueue&Order& orderQ
public OrderQueueMonitor(BlockingQueue&Order& orderQueue) {
this.orderQueue = orderQ
public void start() {
Thread thread = new Thread(this, &Order Queue Monitor&);
thread.start();
@Override public void run() {
while (true) {
TimeUnit.SECONDS.sleep(2);
int size = orderQueue.size();
System.out.println(&Queue size is:& + size);
} catch (InterruptedException e) {
e.printStackTrace();
为了完成Spring框架,我加入了应用上下文,示例代码如下:
&?xml version=&1.0& encoding=&UTF-8&?&
&beans xmlns=&http://www.springframework.org/schema/beans&
xmlns:p=&http://www.springframework.org/schema/p&
xmlns:xsi=&http://www.w3.org/2001/XMLSchema-instance&
xmlns:context=&http://www.springframework.org/schema/context&
xsi:schemaLocation=&http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.1.xsd&
default-init-method=&start&
default-destroy-method=&destroy&&
&bean id=&theQueue&/&
&bean id=&orderProducer&&
&constructor-arg ref=&theQueue&/&
&bean id=&OrderRecorder&&
&constructor-arg ref=&theQueue&/&
&bean id=&QueueMonitor&&
&constructor-arg ref=&theQueue&/&
下一步就是把这个内存泄露的代码跑起来,你需要改变下面的目录:
&your-path&/git/captaindebug/producer-consumer/target/classes
然后输入下面的命令:
java -cp /path-to/spring-beans-3.2.3.RELEASE.jar:/path-to/spring-context-3.2.3.RELEASE.jar:/path-to/spring-core-3.2.3.RELEASE.jar:/path-to/slf4j-api-1.6.1-javadoc.jar:/path-to/commons-logging-1.1.1.jar:/path-to/spring-expression-3.2.3.RELEASE.jar:. com.captaindebug.producerconsumer.problem.Main
“path-to”对应着你的jar文件目录。
Java比较讨厌的一点是,从命令行来运行程序非常的困难——你必须要搞清楚类的目录、选项、需要设定的属性、main所在的类在哪里。当然,有方法能让你只需要输入Java的项目名称,然后Java虚拟机帮你把一切都搞定,特别是使用默认设置:这有多难呢?
你也可以通过附加一个简单的JConsole来监控应用程序的内存泄漏。如果你最近运行过,则需要在上面的命令行中添加如下的选项(选择自己的端口号):
-Dcom.sun.management.jmxremote
-Dcom.sun.management.jmxremote.port=9010
-Dcom.sun.management.jmxremote.local.only=false
-Dcom.sun.management.jmxremote.authenticate=false
-Dcom.sun.management.jmxremote.ssl=false
如果你看看堆的使用量,你会发现随着队列的增大堆逐渐变大。
你可能不会发现1KB的内存泄露,但1GB的内存泄露就很明显了。所以,接下来要做的事情就是等待内存的泄露直到进入下一个阶段的研究。下回见……
原文链接:
- 译文链接: [ 转载请保留原文出处、译者和译文链接。]
关于作者:
(新浪微博:)
好厉害,作为新手小白,真心感谢作者!
关于ImportNew
ImportNew 专注于 Java 技术分享。于日 11:11正式上线。是的,这是一个很特别的时刻 :)
ImportNew 由两个 Java 关键字 import 和 new 组成,意指:Java 开发者学习新知识的网站。 import 可认为是学习和吸收, new 则可认为是新知识、新技术圈子和新朋友……
新浪微博:
推荐微信号
反馈建议:@
广告与商务合作QQ:
– 好的话题、有启发的回复、值得信赖的圈子
– 写了文章?看干货?去头条!
– 为IT单身男女服务的征婚传播平台
– 优秀的工具资源导航
– 活跃 & 专业的翻译小组
– 国内外的精选博客文章
– UI,网页,交互和用户体验
– JavaScript, HTML5, CSS
– 专注Android技术分享
– 专注iOS技术分享
– 专注Java技术分享
– 专注Python技术分享
& 2016 ImportNew

我要回帖

更多关于 handler内存泄露 的文章

 

随机推荐