当前位置:首页>正文

java在什么情况下会出现内存泄露 JAVA 内存泄漏问题。

2023-06-25 15:20:53 互联网 未知

java在什么情况下会出现内存泄露

内存泄露就是指一个不再被程序使用的对象或变量一直被占据在内存中。java中的内存泄露的情况:
1.长生命周期的对象持有短生命周期对象的引用就很可能发生内存泄露,例如,缓存系统,我们加载了一个对象放在缓存中(例如放在一个全局map对象中),然后一直不再使用它,这个对象一直被缓存引用,但却不再被使用。
2.集合类,而如果集合类是全局性的变量(比如类中的静态属性,全局性的map等即有静态引用或final一直指向它),那么没有相应的删除机制,很可能导致集合所占用的内存只增不减,因此提供这样的删除机制或者定期清除策略非常必要。
3.单例模式。不正确使用单例模式是引起内存泄露的一个常见问题,单例对象在被初始化后将在JVM的整个生命周期中存在(以静态变量的方式),如果单例对象持有外部对象的引用,那么这个外部对象将不能被jvm正常回收,导致内存泄露

JAVA 内存泄漏问题。

Java中虽然使用了gc策略,但事实上还是会出现内存泄漏现象的,java因此还提出了弱引用等局部解决方案。

但楼主说的System.exit(0)是不会形成内存泄漏的。

其实这里都是两个范畴的内存了。楼上以及我开始说的Java中的内存是指虚拟机的内存,映射到宿主机可以有各种实现,虽然一般也是映射到内存。 而System.exit(0)会析构掉虚拟机,也就是把这个虚拟的机器都拆了,也就无从谈起虚拟机内存泄漏不泄漏的概念,正所谓皮之不存,毛将焉附。而问题是宿主机的内存是否泄漏了。从原理上说,虚拟机运行时,不管执行怎样的指令,映射到宿主机器资源,都回在机器被拆掉时释放。当然,从实现上说,如果宿主操作系统,或者JVM有bug,当然有可能造成内存泄漏,但和java程序员写的客户程序无关。(补充:包括在宿主机内杀java进程,其资源回收问题是操作系统和java平台的责任。我们在古老的操作系统经常会遇到文件没正常关闭之类的问题,但现在的操作系统这些问题应该不会很大,也就是宿主机其实也有一定的回收机制,包括内存回收,但着本身不是内存泄漏的范畴,内存泄漏是指程序运行时的客户程序造成的内存资源失控。当客户程序退出时的问题,就是操作系统设计的范畴了)

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.arraycopy(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内存泄露与溢出的区别:
内存溢出就是你要求分配的内存超出了系统能给你的,系统不能满足需求,于是产生溢出。ava内存泄漏就是没有及时清理内存垃圾,导致系统无法再给你提供内存资源(内存资源耗尽)。看到上面的解释,可能有些朋友还是不太理解吧。没问题,看以下例子
1. Java内存泄露是说程序逻辑问题,造成申请的内存无法释放.这样的话无论多少内存,早晚都会被占用光的.最简单的例子就是死循环了.由于程序判断错误导经常发生此事    
2. Java内存泄漏是指在堆上分配的内存没有被释放,从而失去对其控制。这样会造成程序能使用的内存越来越少,导致系统运行速度减慢,严重情况会使程序当掉。
3. 关于内存溢出有点出入。比如说你申请了一个integer,但给它存了long才能存下的数,那就是内存溢出。
举个现实中的例子:比如有一个桶,装满了水.你丢个苹果进去。桶的水正常。如果你放个大石头。水就出溢出,内存溢出也就是这个原理。
区别:内存溢出,提供的内存不够;Java内存泄漏,无法再提供内存资源

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 是否提供相应的操作去除引用。

java内存泄露,是什么意思

Java内存泄露
  一般来说内存泄漏有两种情况。一种情况如在C/C 语言中的,在堆中的分配的内存,在没有将其释放掉的时候,就将所有能访问这块内存的方式都删掉(如指针重新赋值);另一种情况则是在内存对象明明已经不需要的时候,还仍然保留着这块内存和它的访问方式(引用)。第一种情况,在Java中已经由于垃圾回收机制的引入,得到了很好的解决。所以,Java中的内存泄漏,主要指的是第二种情况。
  可能光说概念太抽象了,大家可以看一下这样的例子:
  1 Vector v=new Vector(10)
  2 for (int i=1i<100 i ){
  3 Object o=new Object()
  4 v.add(o)
  5 o=null
  6 }
  在这个例子中,代码栈中存在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,导致程序崩溃。
  一般情况下内存泄漏的避免
  在不涉及复杂数据结构的一般情况下,Java的内存泄露表现为一个内存对象的生命周期超出了程序需要它的时间长度。有时也将其称为“对象游离”。
  例如:
  1 public class FileSearch{
  2
  3 private byte[] content
  4 private File mFile
  5
  6 public FileSearch(File file){
  7 mFile = file
  8 }
  9
  10 public boolean hasString(String str){
  11 int size = getFileSize(mFile)
  12 content = new byte[size]
  13 loadFile(mFile, content)
  14
  15 String s = new String(content)
  16 return s.contains(str)
  17 }
  18 }
  在这段代码中,FileSearch类中有一个函数hasString,用来判断文档中是否含有指定的字符串。流程是先将mFile加载到内存中,然后进行判断。但是,这里的问题是,将content声明为了实例变量,而不是本地变量。于是,在此函数返回之后,内存中仍然存在整个文件的数据。而很明显,这些数据后续是不再需要的,这就造成了内存的无故浪费。
  要避免这种情况下的内存泄露,要求以C/C 的内存管理思维来管理自己分配的内存。第一,是在声明对象引用之前,明确内存对象的有效作用域。在一个函数内有效的内存对象,应该声明为local变量,与类实例生命周期相同的要声明为实例变量……以此类推。第二,在内存对象不再需要时,记得手动将其引用置空。
  复杂数据结构中的内存泄露问题
  在实际的项目中,经常用到一些较为复杂的数据结构用于缓存程序运行过程中需要的数据信息。有时,由于数据结构过于复杂,或者存在一些特殊的需求(例如,在内存允许的情况下,尽可能多的缓存信息来提高程序的运行速度等情况),很难对数据结构中数据的生命周期作出明确的界定。这个时候,可以使用Java中一种特殊的机制来达到防止内存泄露的目的。
  之前介绍过,Java的GC机制是建立在跟踪内存的引用机制上的。而在此之前,所使用的引用都只是定义一个“Object o”这样形式的。事实上,这只是Java引用机制中的一种默认情况,除此之外,还有其他的一些引用方式。通过使用这些特殊的引用机制,配合GC机制,就可以达到一些需要的效果。

相关文章