热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

java中的内存泄漏_深入理解Java中的内存泄漏

内存泄漏内存泄漏发生的原因造成内存泄露的常见情形内存泄露的解决方案Java的一个最显著的优势是内存管理。你只需要简单的创建对象而不需要负责释放空间,因为Java的垃圾

内存泄漏

内存泄漏发生的原因

造成内存泄露的常见情形

内存泄露的解决方案

Java的一个最显著的优势是内存管理。你只需要简单的创建对象而不需要负责释放空间,因为Java的垃圾回收器会负责内存的回收。然而,情况并不是这样简单,内存泄露还是经常会在Java应用程序中出现。

内存泄漏

内存泄露的定义:对于应用程序来说,当对象已经不再被使用,但是Java的垃圾回收器不能回收它们的时候,就产生了内存泄露。

要理解这个定义,我们需要理解对象在内存中的状态。如下图所示,展示了哪些对象是无用对象,哪些是未被引用的对象;

e729ccd8c8c48de2e9e2930e0812f4ce.png

未引用对象将会被垃圾回收器回收,而引用对象却不会。未引用对象很显然是无用的对象。然而,无用的对象并不都是未引用对象,有一些无用对象也有可能是引用对象,这部分对象正是内存泄露的来源。

内存泄漏发生的原因

如下图所示,对象A引用对象B,A的生命周期(t1-t4)比B的生命周期(t2-t3)要长,当B在程序中不再被使用的时候,A仍然引用着B。在这种情况下,垃圾回收器是不会回收B对象的,这就可能造成了内存不足问题,因为A可能不止引用着B对象,还可能引用其它生命周期比A短的对象,这就造成了大量无用对象不能被回收,且占据了昂贵的内存资源。

同样的,B对象也可能引用着一大堆对象,这些被B对象引用着的对象也不能被垃圾回收器回收,所有的这些无用对象消耗了大量内存资源。

e96102a1677e543426627e9a5af36a2a.png

造成内存泄露的常见情形

集合类,比如HashMap,ArrayList等,这些对象经常会发生内存泄露。比如当它们被声明为静态对象时,它们的生命周期会跟应用程序的生命周期一样长,很容易造成内存不足。

像HashMap、Vector等的使用最容易出现内存泄露,这些静态变量的生命周期和应用程序一致,他们所引用的所有的对象Object也不能被释放,因为他们也将一直被Vector等引用着。

Static Vector v = new Vector(10);

for (int i &#61; 1; i<100; i&#43;&#43;)

{

Object o &#61; new Object();

v.add(o);

o &#61; null;

}

当集合里面的对象属性被修改后&#xff0c;再调用remove()方法时不起作用。

package 校招;

import java.util.HashSet;

import java.util.Set;

public class MemoryOut{

public static void main(String[] args){

Set set &#61; new HashSet();

Person p1 &#61; new Person("唐僧","pwd1",25);

Person p2 &#61; new Person("孙悟空","pwd2",26);

Person p3 &#61; new Person("猪八戒","pwd3",27);

set.add(p1);

set.add(p2);

set.add(p3);

System.out.println("总共有:"&#43;set.size()&#43;" 个元素!"); //结果&#xff1a;总共有:3 个元素!

p3.setAge(2); //修改p3的年龄,此时p3元素对应的hashcode值发生改变

set.remove(p3); //此时remove不掉&#xff0c;造成内存泄漏

set.add(p3); //重新添加&#xff0c;居然添加成功

System.out.println("总共有:"&#43;set.size()&#43;" 个元素!"); //结果&#xff1a;总共有:4 个元素!

for (Person person : set)

{

System.out.println(person);

}

}

}

class Person{

int age;

String name;

String password;

public Person(String name, String password, int age){

this.name &#61; name;

this.password &#61; password;

this.age &#61; age;

}

public int getAge(){

return age;

}

public void setAge(int age){

this.age &#61; age;

}

&#64;Override

public int hashCode(){

final int prime &#61; 31;

int result &#61; 1;

result &#61; prime * result &#43; age;

return result;

}

&#64;Override

public boolean equals(Object obj){

if (this &#61;&#61; obj)

return true;

if (obj &#61;&#61; null)

return false;

if (getClass() !&#61; obj.getClass())

return false;

Person other &#61; (Person) obj;

if (age !&#61; other.age)

return false;

return true;

}

}

监听器

在java 编程中&#xff0c;我们都需要和监听器打交道&#xff0c;通常一个应用当中会用到很多监听器&#xff0c;我们会调用一个控件的诸如addXXXListener()等方法来增加监听器&#xff0c;但往往在释放对象的时候却没有记住去删除这些监听器&#xff0c;从而增加了内存泄漏的机会。

各种连接

比如数据库连接(dataSourse.getConnection())&#xff0c;网络连接(socket)和io连接&#xff0c;除非其显式的调用了其close()方法将其连接关闭&#xff0c;否则是不会自动被GC 回收的。对于Resultset 和Statement 对象可以不进行显式回收&#xff0c;但Connection 一定要显式回收&#xff0c;因为Connection 在任何时候都无法自动回收&#xff0c;而Connection一旦回收&#xff0c;Resultset 和Statement 对象就会立即为NULL。但是如果使用连接池&#xff0c;情况就不一样了&#xff0c;除了要显式地关闭连接&#xff0c;还必须显式地关闭Resultset Statement 对象(关闭其中一个&#xff0c;另外一个也会关闭)&#xff0c;否则就会造成大量的Statement 对象无法释放&#xff0c;从而引起内存泄漏。这种情况下一般都会在try里面去的连接&#xff0c;在finally里面释放连接。

内部类和外部模块的引用

内部类的引用是比较容易遗忘的一种&#xff0c;而且一旦没释放可能导致一系列的后继类对象没有释放。此外程序员还要小心外部模块不经意的引用&#xff0c;例如程序员A 负责A 模块&#xff0c;调用了B 模块的一个方法如&#xff1a;

public void registerMsg(Object b);

这种调用就要非常小心了&#xff0c;传入了一个对象&#xff0c;很可能模块B就保持了对该对象的引用&#xff0c;这时候就需要注意模块B 是否提供相应的操作去除引用。

单例模式

不正确使用单例模式是引起内存泄漏的一个常见问题&#xff0c;单例对象在初始化后将在JVM的整个生命周期中存在(以静态变量的方式)&#xff0c;如果单例对象持有外部的引用&#xff0c;那么这个对象将不能被JVM正常回收&#xff0c;导致内存泄漏&#xff0c;考虑下面的例子&#xff1a;

class A{

public A(){

B.getInstance().setA(this);

}

....

}

//B类采用单例模式

class B{

private A a;

private static B instance&#61;new B();

public B(){}

public static B getInstance(){

return instance;

}

public void setA(A a){

this.a&#61;a;

}

//getter...

}

显然B采用singleton模式&#xff0c;它持有一个A对象的引用&#xff0c;而这个A类的对象将不能被回收。想象下如果A是个比较复杂的对象或者集合类型会发生什么情况.

内存泄露的解决方案

避免在循环中创建对象。

尽早释放无用对象的引用。 (最基本的建议)

尽量少用静态变量&#xff0c; 因为静态变量存放在永久代(方法区) &#xff0c; 永久代基本不

参与垃圾回收。

使用字符串处理&#xff0c; 避免使用 String&#xff0c; 应大量使用 StringBuffer&#xff0c; 每一个 String

对象都得独立占用内存一块区域。



推荐阅读
  • Iamtryingtomakeaclassthatwillreadatextfileofnamesintoanarray,thenreturnthatarra ... [详细]
  • Java太阳系小游戏分析和源码详解
    本文介绍了一个基于Java的太阳系小游戏的分析和源码详解。通过对面向对象的知识的学习和实践,作者实现了太阳系各行星绕太阳转的效果。文章详细介绍了游戏的设计思路和源码结构,包括工具类、常量、图片加载、面板等。通过这个小游戏的制作,读者可以巩固和应用所学的知识,如类的继承、方法的重载与重写、多态和封装等。 ... [详细]
  • 本文讨论了一个关于cuowu类的问题,作者在使用cuowu类时遇到了错误提示和使用AdjustmentListener的问题。文章提供了16个解决方案,并给出了两个可能导致错误的原因。 ... [详细]
  • Java容器中的compareto方法排序原理解析
    本文从源码解析Java容器中的compareto方法的排序原理,讲解了在使用数组存储数据时的限制以及存储效率的问题。同时提到了Redis的五大数据结构和list、set等知识点,回忆了作者大学时代的Java学习经历。文章以作者做的思维导图作为目录,展示了整个讲解过程。 ... [详细]
  • 本文介绍了OC学习笔记中的@property和@synthesize,包括属性的定义和合成的使用方法。通过示例代码详细讲解了@property和@synthesize的作用和用法。 ... [详细]
  • Spring特性实现接口多类的动态调用详解
    本文详细介绍了如何使用Spring特性实现接口多类的动态调用。通过对Spring IoC容器的基础类BeanFactory和ApplicationContext的介绍,以及getBeansOfType方法的应用,解决了在实际工作中遇到的接口及多个实现类的问题。同时,文章还提到了SPI使用的不便之处,并介绍了借助ApplicationContext实现需求的方法。阅读本文,你将了解到Spring特性的实现原理和实际应用方式。 ... [详细]
  • PHP中的单例模式与静态变量的区别及使用方法
    本文介绍了PHP中的单例模式与静态变量的区别及使用方法。在PHP中,静态变量的存活周期仅仅是每次PHP的会话周期,与Java、C++不同。静态变量在PHP中的作用域仅限于当前文件内,在函数或类中可以传递变量。本文还通过示例代码解释了静态变量在函数和类中的使用方法,并说明了静态变量的生命周期与结构体的生命周期相关联。同时,本文还介绍了静态变量在类中的使用方法,并通过示例代码展示了如何在类中使用静态变量。 ... [详细]
  • 本文介绍了Java高并发程序设计中线程安全的概念与synchronized关键字的使用。通过一个计数器的例子,演示了多线程同时对变量进行累加操作时可能出现的问题。最终值会小于预期的原因是因为两个线程同时对变量进行写入时,其中一个线程的结果会覆盖另一个线程的结果。为了解决这个问题,可以使用synchronized关键字来保证线程安全。 ... [详细]
  • [大整数乘法] java代码实现
    本文介绍了使用java代码实现大整数乘法的过程,同时也涉及到大整数加法和大整数减法的计算方法。通过分治算法来提高计算效率,并对算法的时间复杂度进行了研究。详细代码实现请参考文章链接。 ... [详细]
  • 标题: ... [详细]
  • Android源码深入理解JNI技术的概述和应用
    本文介绍了Android源码中的JNI技术,包括概述和应用。JNI是Java Native Interface的缩写,是一种技术,可以实现Java程序调用Native语言写的函数,以及Native程序调用Java层的函数。在Android平台上,JNI充当了连接Java世界和Native世界的桥梁。本文通过分析Android源码中的相关文件和位置,深入探讨了JNI技术在Android开发中的重要性和应用场景。 ... [详细]
  • Java学习笔记之面向对象编程(OOP)
    本文介绍了Java学习笔记中的面向对象编程(OOP)内容,包括OOP的三大特性(封装、继承、多态)和五大原则(单一职责原则、开放封闭原则、里式替换原则、依赖倒置原则)。通过学习OOP,可以提高代码复用性、拓展性和安全性。 ... [详细]
  • 本文介绍了iOS数据库Sqlite的SQL语句分类和常见约束关键字。SQL语句分为DDL、DML和DQL三种类型,其中DDL语句用于定义、删除和修改数据表,关键字包括create、drop和alter。常见约束关键字包括if not exists、if exists、primary key、autoincrement、not null和default。此外,还介绍了常见的数据库数据类型,包括integer、text和real。 ... [详细]
  • 开发笔记:加密&json&StringIO模块&BytesIO模块
    篇首语:本文由编程笔记#小编为大家整理,主要介绍了加密&json&StringIO模块&BytesIO模块相关的知识,希望对你有一定的参考价值。一、加密加密 ... [详细]
  • 本文讨论了在openwrt-17.01版本中,mt7628设备上初始化启动时eth0的mac地址总是随机生成的问题。每次随机生成的eth0的mac地址都会写到/sys/class/net/eth0/address目录下,而openwrt-17.01原版的SDK会根据随机生成的eth0的mac地址再生成eth0.1、eth0.2等,生成后的mac地址会保存在/etc/config/network下。 ... [详细]
author-avatar
ck凯悦风_855
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有