① 如何用java编写一段代码引发内存泄露
1、首先得搞清楚什么叫内存泄露,简单来说就是一个东西放塌液在内存里的时间太长了,当你的程序都跑完了,它还存在那里。这时它是白白的占用了你的内存,累积起来占用的内存越来越多……最后就会导致JVM报错:out of memory。
2、一般情况下,别人如果能指出你的系统(程序)内存溢出,这个人应该还是挺厉害槐渗的。通常对于新人来说,喜欢把变量直接定义在class下(此时称之为实例变量,或者成员变量),那么在方法里调用后,这个实例变量是不会被释放的,大量的这样使用就可能会引发内存泄露。
3、把变量定义在方法里,当这个方法执行完毕后内存就得到释放了,这是个好习惯。
4、如果想要看到内存溢出,可以按这样的思路去尝试一下:定义一个静态的实例变量(list或其它集合),然后在一个方法里循环往这个静态变量塞东西,直到这个实例变量撑爆你的jvm内存。很团明物快你就能看到out of memory……
import java.util.ArrayList;
import java.util.List;
public class MemoryTest {
private static List list = new ArrayList();
private static int count = 0;
public static void main(String[] args) throws InterruptedException {
System.out.println("申请前的可用内存 = "+getFreeMemory());
while(true){
list.add(new byte[1024*1024]);//用实例变量申请1M内存,当方法执行完毕时,这个static的变量是不会被释放
count++;
if (count % 100 == 0) {
System.out.println("当前list.size()="+list.size()+",可用内存 = "+getFreeMemory());
Thread.sleep(500);
}
}
}
public static long getFreeMemory() {
return Runtime.getRuntime().freeMemory() / (1024 * 1024);
}
}
② java内存泄漏怎么处理
一、Java内存回收机制
不论哪种语言的内存分配方式,都需要返回所分配内存的真实地址,也就是返回一个指针到内存块的首地址。Java中对象是采用new或者反射的方法创建的,这些对象的创建都是在堆(Heap)中分配的,所有对象的回收都是由Java虚羡逗拟机通过垃圾回收机制完成的。GC为了能够正确释放对象,会监控每个对象的运行状况,对他们的申请、引用、被引用、赋值等状况进行监控,Java会使用有向图的方法进行管理内存,实时监控对象是否可以达到,如果不可到达,则就将其回收,这样也可以消除引用循环的问题。在Java语言中,判断一个内存空间是否符合垃圾收集标准有两个:一个是给对象赋予了空值null,以下再没有调用过,另一个是给对象赋予了新值,这样重新分配了内存空间。
二、Java内存泄露引起原因
首先,什么是内存泄露看经常听人谈起内存泄露,但要问什么是内存泄露,没几个说得清楚。内存泄露是指无用对象(不再使用的对象)持续占有内存或无用对象的内存得不到及时释放,从而造成的内存空间的浪费称为内存泄露。内存泄露有时不严重且不易察觉,这样开发者就不知道存在内存泄兄散卖露,但有时也会很严重,会提示你Out of memory。
那么,Java内存泄露根本原因是什么呢看长生命周期的对象持有短生命周期对象的引用就很可能发生内存泄露,尽管短生命周期对象已经不再需要,但是因为长生命周期对象持有它的引用而导致不能被回收,这就是java中内存泄露的发生场景。具体主要有如下几大类:
1、静态集合类引起内存泄露:
像HashMap、Vector等的使用最容易出现内存泄露,这些静态变量的生命周期和应用程序一致,他们所引用的所有的掘拦对象Object也不能被释放,因为他们也将一直被Vector等引用着。
例:
Static Vector v = new Vector(10);
for (int i = 1; i<100; i++)
{
Object o = new Object();
v.add(o);
o = null;
}//
在这个例子中,循环申请Object 对象,并将所申请的对象放入一个Vector 中,如果仅仅释放引用本身(o=null),那么Vector 仍然引用该对象,所以这个对象对GC 来说是不可回收的。因此,如果对象加入到Vector 后,还必须从Vector 中删除,最简单的方法就是将Vector对象设置为null。
2、当集合里面的对象属性被修改后,再调用remove()方法时不起作用。
例:
public static void main(String[] args)
{
Set set = new HashSet();
Person p1 = new Person("唐僧","pwd1",25);
Person p2 = new Person("孙悟空","pwd2",26);
Person p3 = new Person("猪八戒","pwd3",27);
set.add(p1);
set.add(p2);
set.add(p3);
System.out.println("总共有:"+set.size()+" 个元素!"); //结果:总共有:3 个元素!
p3.setAge(2); //修改p3的年龄,此时p3元素对应的hashcode值发生改变
set.remove(p3); //此时remove不掉,造成内存泄漏
set.add(p3); //重新添加,居然添加成功
System.out.println("总共有:"+set.size()+" 个元素!"); //结果:总共有:4 个元素!
for (Person person : set)
{
System.out.println(person);
}
}
3、监听器
在java 编程中,我们都需要和监听器打交道,通常一个应用当中会用到很多监听器,我们会调用一个控件的诸如addXXXListener()等方法来增加监听器,但往往在释放对象的时候却没有记住去删除这些监听器,从而增加了内存泄漏的机会。
4、各种连接
比如数据库连接(dataSourse.getConnection()),网络连接(socket)和io连接,除非其显式的调用了其close()方法将其连接关闭,否则是不会自动被GC 回收的。对于Resultset 和Statement 对象可以不进行显式回收,但Connection 一定要显式回收,因为Connection 在任何时候都无法自动回收,而Connection一旦回收,Resultset 和Statement 对象就会立即为NULL。但是如果使用连接池,情况就不一样了,除了要显式地关闭连接,还必须显式地关闭Resultset Statement 对象(关闭其中一个,另外一个也会关闭),否则就会造成大量的Statement 对象无法释放,从而引起内存泄漏。这种情况下一般都会在try里面去的连接,在finally里面释放连接。
5、内部类和外部模块等的引用
内部类的引用是比较容易遗忘的一种,而且一旦没释放可能导致一系列的后继类对象没有释放。此外程序员还要小心外部模块不经意的引用,例如程序员A 负责A 模块,调用了B 模块的一个方法如:
public void registerMsg(Object b);
这种调用就要非常小心了,传入了一个对象,很可能模块B就保持了对该对象的引用,这时候就需要注意模块B 是否提供相应的操作去除引用。
6、单例模式
不正确使用单例模式是引起内存泄露的一个常见问题,单例对象在被初始化后将在JVM的整个生命周期中存在(以静态变量的方式),如果单例对象持有外部对象的引用,那么这个外部对象将不能被jvm正常回收,导致内存泄露,考虑下面的例子:
class A{
public A(){
B.getInstance().setA(this);
}
....
}
//B类采用单例模式
class B{
private A a;
private static B instance=new B();
public B(){}
public static B getInstance(){
return instance;
}
public void setA(A a){
this.a=a;
}
//getter...
}
显然B采用singleton模式,它持有一个A对象的引用,而这个A类的对象将不能被回收。想象下如果A是个比较复杂的对象或者集合类型会发生什么情况
③ java中是否会存在内存泄漏
会。java导致内存泄露的原因很明确:长生命周期的对象持有短生命周期对象的引用就很可能发生内存泄露,尽管短生命周期对象已经不再需要,但是因为长生命周期对象持有它的引用而导致不能被回收,这就是java中内存泄露的发生场景。
1.集合类,集合类仅仅有添加元素的方法,而弊返察没有相应的删除机制,导致内存被占用。这一点其实也不明确,这个集合类如果仅仅是局部变量,根本不会造成内存泄露,在方法栈退出后就没有引用了会被jvm正常回收。而如果这个集合类是全局性的变量(比如类中的静态属性,全局性的map等即有静态引用或final一直指向它),那么没有相应的删除机制,很可能导致集合所占用的内存只增不减,因此提供这样的删除机制或者定期清除策略非常必要。
2.单例模式。不正确使用单例模式是引起内存泄露的一个常见问题,单例对象在被初始化后将在JVM的整个生命周期中存在(以静态变量的方式),如果单例对象持有外部对世宴象的引用,那么这个外部对象将不能租茄被jvm正常回收,导致内存泄露,考虑下面的例子:class A{
public A(){
B.getInstance().setA(this);}
....}
//B类采用单例模式class B{
private A a;
private static B instance=new B();
public B(){}
public static B getInstance(){
return instance;}
④ Android技术分享|Android 中部分内存泄漏示例及解决方案
内存泄漏:
举例:
请注意以下的例子是虚构的
内存抖动
源自Android文档中的 Memory churn 一词,中文翻译为内存抖动。
指快速频繁的创建对象从而产生的性能问题。
引用Android文档原文:
Java内存泄漏的根本原因是 长生命周期 的对象持有 短生命周期 对象的引用就很可能发生内存泄漏。
尽管短生命周期对象已经不再需要,但因为长生命周期依旧持有它的引用,故不能被回收而导致内存泄漏。
静态集合类引起的内存泄漏
如果仅仅释放引用本身(tO = null), ArrayList 依然在引用该对象,GC无法回收。
监听器
在Java应用中,通常会用到很多监听器,一般通过 addXXXXListener() 实现。但释放对象时通常会忘记删除监听器,从而增加内存泄漏的风险。
各种连接
如数据库连接、网络连接(Socket)和I/O连接。忘记显式调用 close() 方法引起的内存泄漏。
内部类和外部模块的引用
内部类的引用是很容易被遗忘的一种,一旦没有释放可能会导致一系列后续对象无法释放。此外还要小心外部模块不经意的引用,内部类是否提供相应的操作去除外部引用。
单例模式
由于单例的静态特性,使其生命周期与应用的生命周期一样长,一旦使用不恰当极易造成内存泄漏。如果单利持有外部引用,需要注意提供释放方式,否则当外部对象无法被正常回收时,会进而导致内存泄漏。
集合类泄漏
如集合的使用范围超过逻辑代码的范围,需要格外注意删除机制是否完善可靠。比如由静态属性 static 指向的集合。
单利泄漏
以下为简单逻辑代码,只为举例说明内存泄漏问题,不保证单利模式的可靠性。
AppManager 创建时需要传入一个 Context ,这个 Context 的生命周期长短至关重要。
1. 如果传入的是 Application 的 Context ,因为 Application 的生命周期等同于应用的生命周期,所以没有任何问题。
2. 如果传入的是 Activity 的 Context ,则需要考虑这个 Activity 是否在整个生命周期都不会被回收了,如果不是,则会造成内存泄漏。
非静态内部类创建静态实例造成的内存泄漏
应该将该内部类单独封装为一个单例来使用。
匿名内部类/异步线程
Runnable都使用了匿名内部类,将持有MyActivity的引用。如果任务在Activity销毁前未完成,将导致Activity的内存无法被回收,从而造成内存泄漏。
解决方法:将Runnable独立出来或使用静态内部类,可以避免因持有外部对象导致的内存泄漏。
Handler造成的内存泄漏
Handler属于TLS(Thread Local Storage)变量,生命周期与Activity是不一致的,容易导致持有的对象无法正确被释放
当Android应用程序启动时,该应用程序的主线程会自动创建一个Looper对象和与之关联的MessageQueue。
当主线程中实例化一个Handler对象后,它就会自动与主线程Looper的MessageQueue关联起来。所有发送到MessageQueue的Messag都会持有Handler的引用,所以Looper会据此回调Handle的handleMessage()方法来处理消息。只要MessageQueue中有未处理的Message,Looper就会不断的从中取出并交给Handler处理。
另外,主线程的Looper对象会伴随该应用程序的整个生命周期。
在Java中,非静态内部类和匿名类内部类都会潜在持有它们所属的外部类的引用,但是静态内部类却不会。
当该 Activity 被 finish() 掉时,延迟执行任务的 Message 还会继续存在于主线程中,它持有该 Activity 的 Handler 引用,所以此时 finish() 掉的 Activity 就不会被回收了从而造成内存泄漏(因 Handler 为非静态内部类,它会持有外部类的引用,在这里就是指 SampleActivity)。
避免不必要的静态成员变量
对于BroadcastReceiver、ContentObserver、File、Cursor、Stream、Bitmap等资源的使用,应在Activity销毁前及时关闭或注销。
不使用WebView对象时,应调用`destroy()`方法销毁。
⑤ 如何识别Java中的内存泄漏
JAVA有局带侍GC,所以很少会发生内存桐吵泄漏的情况。
主要看程序运行过程中内存的使用量是行伏否随着时间的增加而增加。
⑥ java内存泄漏怎么解决
一般情况下内存泄漏的避免
在不涉及复杂数据结构茄哪的一般情况下,Java 的内存泄露表现为一个内存对象的生命周期超出了程序需要它的时间长度。我们有时也将其称为“对象游离”。
例如:
public class FileSearch{ private byte [] content; private File mFile; public FileSearch(File file){ mFile = file; } public boolean hasString(String str){ int size = getFileSize(mFile); content = new byte [size]; loadFile(mFile, content); String s = new String(content); return s.contains(str); }}
在这段代码中,FileSearch 类中有一个函数 hasString ,用来判断文档中是否含有指定的字符串。流程是先将mFile 加载到内存中,然后进行判断。但是,这里的问题是,将 content 声明为了实例变量,而不是本地变量。于是,在此函数返回之后,内存中仍然存在整个文件的数据。而很明显,这些数据我们后续是不再需要的,这就造成了内存的无故浪费。
要避免这种情况下的内存泄露,要求我们以C/C++ 的内存管理思维来管理自己分配的内存。第一,是在声明对象引用之前,明确内存对象的有效作用域。在一个函数内有效的内存对象,应该声明为 local 变量,与类实例生命周期相同的要声明为实例变量……以此类推。第二,在内存对象不再需要时,记得手动将其引用置空。
复杂数据结构中的内存泄露问题
在实际的项目中,我们经常用到一些较为复杂的数据结构用于缓存程序运行过程中需要的数据信息。有时,由于数据结构过于复杂,或者我们存在一些特殊的需求(例如,在内存允许的情况下,尽可能多的缓存信息来提高程序的运行速度等情况),我们很难对数据结构中数据的生命周期作出明确的界定。这个时候,我们可以使用Java 中一种特殊的机制来达到防止内存泄露的目的。
之前我们介绍过,Java 的 GC 机制是建立在跟踪内存的引用机制上的。而在此之前,我们所使用的引用都只是定义一个“ Object o; ”这样形式的。事实上,这只是 Java 引用机制中的一种默认情况,除此之外,还有其他的一些引用方式。通过使用这些特殊的引用机制,配合 GC 机制,就可以达到一些我改仔们需要的效果。
Java中的几种引用方式
Java中有几种不同的引用方式,它们分别是:强引用、软引用、弱引用和虚引用。下面,我们首先详细地了解下这几种引用方式的意义。
强引用
在此之前我们介绍的内容中所使用的引用 都是强引用,这是使用最普遍的引用。如果一个对象具有强引用,那就类似于必不可少的生活用品,垃圾回收器绝不会回收它。当内存空 间不足,Java 虚拟机宁愿抛出 OutOfMemoryError 错误,使程序异常终止,也不会靠随核纳汪意回收具有强引用的对象来解决内存不足问题。
软引用(SoftReference )
SoftReference 类的一个典型用途就是用于内存敏感的高速缓存。SoftReference 的原理是:在保持对对象的引用时保证在 JVM 报告内存不足情况之前将清除所有的软引用。关键之处在于,垃圾收集器在运行时可能会(也可能不会)释放软可及对象。对象是否被释放取决于垃圾收集器的算法 以及垃圾收集器运行时可用的内存数量。
弱引用(WeakReference )
WeakReference 类的一个典型用途就是规范化映射( canonicalized mapping )。另外,对于那些生存期相对较长而且重新创建的开销也不高的对象来说,弱引用也比较有用。关键之处在于,垃圾收集器运行时如果碰到了弱可及对象,将释放 WeakReference 引用的对象。然而,请注意,垃圾收集器可能要运行多次才能找到并释放弱可及对象。
虚引用(PhantomReference )
PhantomReference 类只能用于跟踪对被引用对象即将进行的收集。同样,它还能用于执行 pre-mortem 清除操作。PhantomReference 必须与 ReferenceQueue 类一起使用。需要 ReferenceQueue 是因为它能够充当通知机制。当垃圾收集器确定了某个对象是虚可及对象时, PhantomReference 对象就被放在它的 ReferenceQueue 上。将 PhantomReference 对象放在 ReferenceQueue 上也就是一个通知,表明 PhantomReference 对象引用的对象已经结束,可供收集了。这使您能够刚好在对象占用的内存被回收之前采取行动。Reference与 ReferenceQueue 的配合使用。
GC、 Reference 与 ReferenceQueue 的交互
A、 GC无法删除存在强引用的对象的内存。
B、 GC发现一个只有软引用的对象内存,那么:
① SoftReference对象的 referent 域被设置为 null ,从而使该对象不再引用 heap 对象。
② SoftReference引用过的 heap 对象被声明为 finalizable 。
③ 当 heap 对象的 finalize() 方法被运行而且该对象占用的内存被释放, SoftReference 对象就被添加到它的 ReferenceQueue (如果后者存在的话)。
C、 GC发现一个只有弱引用的对象内存,那么:
① WeakReference对象的 referent 域被设置为 null , 从而使该对象不再引用heap 对象。
② WeakReference引用过的 heap 对象被声明为 finalizable 。
③ 当heap 对象的 finalize() 方法被运行而且该对象占用的内存被释放时, WeakReference 对象就被添加到它的 ReferenceQueue (如果后者存在的话)。
D、 GC发现一个只有虚引用的对象内存,那么:
① PhantomReference引用过的 heap 对象被声明为 finalizable 。
② PhantomReference在堆对象被释放之前就被添加到它的 ReferenceQueue 。
值得注意的地方有以下几点:
1、 GC 在一般情况下不会发现软引用的内存对象,只有在内存明显不足的时候才会发现并释放软引用对象的内存。
2、 GC 对弱引用的发现和释放也不是立即的,有时需要重复几次 GC ,才会发现并释放弱引用的内存对象。3、软引用和弱引用在添加到 ReferenceQueue 的时候,其指向真实内存的引用已经被置为空了,相关的内存也已经被释放掉了。而虚引用在添加到 ReferenceQueue 的时候,内存还没有释放,仍然可以对其进行访问。
代码示例
通过以上的介绍,相信您对Java 的引用机制以及几种引用方式的异同已经有了一定了解。光是概念,可能过于抽象,下面我们通过一个例子来演示如何在代码中使用 Reference 机制。
String str = new String( " hello " ); // ①ReferenceQueue < String > rq = new ReferenceQueue < String > (); // ②WeakReference < String > wf = new WeakReference < String > (str, rq); // ③str = null ; // ④取消"hello"对象的强引用String str1 = wf.get(); // ⑤假如"hello"对象没有被回收,str1引用"hello"对象// 假如"hello"对象没有被回收,rq.poll()返回nullReference <? extends String > ref = rq.poll(); // ⑥
在以上代码中,注意⑤⑥两处地方。假如“hello ”对象没有被回收 wf.get() 将返回“ hello ”字符串对象, rq.poll() 返回 null ;而加入“ hello ”对象已经被回收了,那么 wf.get() 返回 null , rq.poll() 返回 Reference 对象,但是此 Reference 对象中已经没有 str 对象的引用了 ( PhantomReference 则与WeakReference 、 SoftReference 不同 )。
引用机制与复杂数据结构的联合应用
了解了GC 机制、引用机制,并配合上 ReferenceQueue ,我们就可以实现一些防止内存溢出的复杂数据类型。
例如,SoftReference 具有构建 Cache 系统的特质,因此我们可以结合哈希表实现一个简单的缓存系统。这样既能保证能够尽可能多的缓存信息,又可以保证 Java 虚拟机不会因为内存泄露而抛出 OutOfMemoryError 。这种缓存机制特别适合于内存对象生命周期长,且生成内存对象的耗时比较长的情况,例如缓存列表封面图片等。对于一些生命周期较长,但是生成内存对象开销不大的情况,使用WeakReference 能够达到更好的内存管理的效果。
附SoftHashmap 的源码一份,相信看过之后,大家会对 Reference 机制的应用有更深入的理解。
⑦ 如何编写一个java内存泄露程序
自己改一下下面的代码,把堆栈中的元素改成mp3类型的或更大点的东西
4.Java中参数都是传值的。
对于基本类型,大家基本上没有异议,但是对于引用类型我们也不能有异议。
Java内存泄露情况
JVM回收算法 是很复杂的,我也不知道他们怎么实现的,但是我只知道他们要实现的就是:对于没有被引用的对象是可以回收的。所以你要造成内存泄露就要做到:
持有对无用对象的引用!
不要以为这个很轻易做到,既然无用,你怎么还会持有它的引用? 既然你还持有它,它怎么会是无用的呢?
以下以堆栈更经典这个经典的例子来剖析。
Java代码
public class Stack {
private Object[] elements=new Object[10];
private int size = 0;
public void push(Object e){
ensureCapacity();
elements[size++] = e;
}
public Object pop(){
if( size == 0)
throw new EmptyStackException();
return elements[--size];
}
private void ensureCapacity(){
if(elements.length == size){
Object[] oldElements = elements;
elements = new Object[2 * elements.length+1];
System.array(oldElements,0, elements, 0, size);
}
}
}
上面的原理应该很简单,假如堆栈加了10个元素,然后全部弹出来,虽然堆栈是空的,没有我们要的东西,但是这是个对象是无法回收的,这个才符合了内存泄露的两个条件:无用,无法回收。
但是就是存在这样的东西也不一定会导致什么样的后果,假如这个堆栈用的比较少,也就浪费了几个K内存而已,反正我们的内存都上G了,哪里会有什么影响,再说这个东西很快就会被回收的,有什么关系。下面看两个例子。
例子1
Java代码
public class Bad{
public static Stack s=Stack();
static{
s.push(new Object());
s.pop(); //这里有一个对象发生内存泄露
s.push(new Object()); //上面的对象可以被回收了,等于是自愈了
}
}
因为是static,就一直存在到程序退出,但是我们也可以看到它有自愈功能 ,就是说假如你的Stack最多有100个对象,那么最多也就只有100个对象无法被回收其实这个应该很轻易理解,Stack内部持有100个引用,最坏的情况就是他们都是无用的,因为我们一旦放新的银氏迟进取,以前的引用自然消失!
例子2
Java代码
public class NotTooBad{
public void doSomething(){
Stack s=new Stack();
s.push(new Object());
//other code
s.pop();//这里同样导致对象无法回收,内存泄露.
}//退出方法,s自动无效,s可以被回收,Stack内部的引用自然没了,所以
//这里也可以自愈,而且可以说这个方法不存在内存泄露问题,不过是晚一点
//交给GC而已,因为它是封闭的,对外不开放,可以说上面的代码核告99.9999%的
//情况是不会造成任何影响的,当然你写这样的代码不会有什么坏的影响,但是
//绝对可以说是垃圾代码!没有矛盾吧,我在里面加一个空的for循环也不会有
//什么锋李太大的影响吧,你会这么做吗?
}
⑧ java内存泄露是什么意思
Java内存泄露x0dx0a一般来说内存泄漏有两种情况。一种情况如在C/C++语言中的,在堆中的分配的内存,在没有将其释放掉的时候,就将所有能访问这块内存的方式都删掉(如指针重新赋值);另一种情况则是在内存对象明明已经不需要的时候,还仍然保留着这块内存和它的访问方纳悄式(引用)。第一种情况,在Java中已经由于垃圾回收机制的引入,得到了很好的解决。所以,Java中的内存泄漏,主要指的是第二种情况。x0dx0a可能光说概念太抽象了,大家可以看一下这样的例子:x0dx0a1Vectorv=newVector(10);x0dx0a2for(inti=1;i<100;i++){x0dx0a3Objecto=newObject();x0dx0a4v.add(o);x0dx0a5o=null;x0dx0a6}x0dx0a在这个例子中,代码栈中存在Vector对象的引用v和Object对象的引用o。在For循环中,不断的生成新的对象,然后将其添加到Vector对象中,之后将o引用置空。问题是当o引用被置空后,如果发生GC,创建的Object对象是否能够被GC回收呢?答案是否定的。因为,GC在跟踪代码栈中的引用时,会发现v引用,而继续往下跟踪,就会发现v引用指向的内存空间中又存在指向Object对象的引用。也就是说尽管o引用已经被置空,但是Object对象仍然存在其他的引用,是可以被访问到的,所以GC无法将其释放掉。如果在此循环之后,Object对象对程序已经没有任何作用,那么就认为此Java程序发生了内存泄漏。x0dx0a尽管对于C/C++中的内存泄露情况来说,Java内存泄露导致的破坏性小,除了少数情况会出现程序崩溃的情况外,大多数情况下程序仍然能正常运行。但是,在移动设备对于内存和CPU都有较严格的限制的情况下,Java的内存溢出会导致程序效率低下、占用大量不需要的内存等问题。这将导致整个机器性能变差,严重的也会引起抛出OutOfMemoryError,导致程序崩溃。x0dx0a一般情况下内存泄漏的避免x0dx0a在不涉及复杂数据结构的一般情况下,Java的内存泄露表现为一个内存对象的生命周期超出了程序需要它的时间长度。有时也将其称为“对象游离”。x0dx0a例如:x0dx0a1publicclassFileSearch{x0dx0a2x0dx0a3privatebyte[]content;x0dx0a4privateFilemFile;(Filefile){x0dx0a7mFile=file;x0dx0a8}(Stringstr){x0dx0a11intsize=getFileSize(mFile);x0dx0a12content=newbyte[size];x0dx0a13loadFile(mFile,content);x0dx0a14x0dx0a15Strings=newString(content);x0dx0a16returns.contains(str);x0dx0a17}x0dx0a18}x0dx0a在这段代码中,FileSearch类中有一个函数hasString,用来判断文档中是否含有指定的字符串。流程是先将mFile加载到内存中,然后进行判断。但是,这里瞎猛的问题是,将content声明为了实例变量,而不是本地变量。于是,在此函数返回之后,内存中仍然存在整个文件的数据。而很明显,这些数据后续是不再需要的,这就造成了内存的无故浪费。x0dx0a要避免这种情况下的内存泄露,要求以C/C++的内存管理思维来管理自己分配的内存。第一,是在声明对象引用之前,明确内存对象的有效作用域。在一个函数内有效的内存对象,应该声明为local变量,与类实例生命周期相同的要声明为实例变量??以此类推。第二,在内存对象不再需要时,记得手动将其引用置空。x0dx0a复杂数据结构中的内存泄露问题x0dx0a在实际的项目洞神渣中,经常用到一些较为复杂的数据结构用于缓存程序运行过程中需要的数据信息。有时,由于数据结构过于复杂,或者存在一些特殊的需求(例如,在内存允许的情况下,尽可能多的缓存信息来提高程序的运行速度等情况),很难对数据结构中数据的生命周期作出明确的界定。这个时候,可以使用Java中一种特殊的机制来达到防止内存泄露的目的。x0dx0a之前介绍过,Java的GC机制是建立在跟踪内存的引用机制上的。而在此之前,所使用的引用都只是定义一个“Objecto;”这样形式的。事实上,这只是Java引用机制中的一种默认情况,除此之外,还有其他的一些引用方式。通过使用这些特殊的引用机制,配合GC机制,就可以达到一些需要的效果。
⑨ 如何模拟一个Java内存泄露的情景
importjava.util.ArrayList;
importjava.util.List;
/**
*常见的内存泄露:
*1.文件流不关闭
*2.数据库连接没有关闭
*3.内存使用过多
*/
publicclassTest{
publicstaticvoidmain(String[]args)throwsInterruptedException{
List<int[]>list=newArrayList<int[]>();
Runtimerun=Runtime.getRuntime();
inti=1;
while(true){
int[]arr=newint[1024*8];
list.add(arr);
if(i++%1000==0渣卖蔽){
System.out.print("最大内存="+run.maxMemory()/1024/1024+"M,");
System.out.print("已分配内存="+run.totalMemory()/1024/1024+"M,");
配碧System.out.print("剩余空间内存="+run.freeMemory()/1024/1024+"M");
如州System.out.println("最大可用内存="+(run.maxMemory()-run.totalMemory()+run.freeMemory())/1024/1024+"M");
}
}
}
}