记忆泄漏概念 - 初学者水平

时间:2014-03-11 08:18:29

标签: java memory-leaks

我是一名初级程序员并试图理解内存泄漏的概念 - 主要是Java,因为我目前正在研究Java。是否有人能够帮助我这些例子是否是内存泄漏?

Student s1, s2;

//case I
s1 = new Student("John",20);
s1 = new Student("Mark",19);
// did I just lose the address to John, 20?


//case II:
s1 = new Student("John",20);
s2 = new Student("Mark",19);

s2 = s1;
// did I just lose the address to Mark,19?

3 个答案:

答案 0 :(得分:1)

不,他们不是内存泄漏。分配对象时无法进行内存泄漏。因为你没有保留对第一个分配的学生(在第一个例子中)或第二个学生(在第二个例子中)的任何引用,所以这个实例可以被破坏,没有泄漏。

现在,想象一下

s1 = new Student("John",20);
myHashMap.put(s1, 33);
s1 = new Student("Mark",19);
myHashMap.put(s1, 43);

然后你保留对s1的第一个版本的引用,如果你继续创建学生并且不清理地图,那么你有内存泄漏。保存在内存中的映射是内存泄漏的常见情况。

请注意,在一个理智的java程序中发生内存泄漏并不是那么频繁。

答案 1 :(得分:0)

在C中,您必须告诉运行时恢复内存。所以程序可能如下所示:

char *pointer = malloc(1024); // ask for a chunk of memory
doSomethingWith(pointer);     // something using the memory
free(pointer);
pointer = malloc(2048); // ask for another chunk of memory
doSomethingWith(pointer);     // something using the memory
free(pointer);

如果您未能调用free(),那么该块内存将在程序的生命周期内保持使用:

char *pointer = malloc(1024); // ask for a chunk of memory
doSomethingWith(pointer);     // something using the memory
pointer = malloc(2048); // ask for another chunk of memory

在上面的代码中,假设doSomethingWith()没有将其保存在某处,我们丢失了指向1024字节块的指针,因为我们为pointer分配了一个新值。没有这个价值,我们就无法释放那段记忆。

正如您可以想象的那样,在实际代码中,除非您保持非常有条理,否则很容易丢失所有指针。当代码遵循其快乐路径时,您可以很好地释放内存,但是当它处理错误(找不到文件;主机不可用等)时,它可能会错过free()。这就是导致程序逐渐消耗越来越多无法释放的内存的原因。

  char *buffer = malloc(1024);
  ssize_t size = read(filehandle,buffer,1024,0)
  if( size != -1 ) {
       doSomethingWith(buffer);
       free(buffer);
  } else {
       perror("Error: ");
       // oops, we have not free()d buffer
  }

这称为内存泄漏

在Java中,new大致相当于malloc,并且没有free()。相反,JVM的一部分称为垃圾收集器,它跟踪每个对象,以及每个对象(或者用C语言,“指针”)。当GC决定没有线程直接或间接引用特定对象时,它会回收该对象占用的内存。

因此:

 Person p = new Person("John"); // "John" won't be GCd because p refers to it
 p = new Person("Peter"); // Now nothing refers to "John" to the GC can clear it away

当参考文献超出范围时,参考文献也不复存在:

  Person someMethod() {
      Person p1 = new Person("John");
      Person p2 = new Person("Peter");
      return p2; // the caller will probably use the reference to p2,
                 // but after this method ends, p1 is definitely available
                 // for GC.
  }

所有这些意味着编写泄漏内存的Java程序非常困难。

垃圾收集器会定期进行清理,您可以使用 VisualVM 等工具查看它。你会得到一个“锯齿”的效果,每次GC运行时内存使用量会累积,然后急剧下降。

在Java中泄漏内存的一种方法是将引用存储在您不需要的某些结构中,或者不能正确维护。

 Person p = new Person("John");
 irrelevantSet.add(p);
 p = new Person("Peter");

此处,GC无法清除“John”,因为irrelevantSet包含对它的引用。通常你将一个对象放在一个像Set这样的结构中,因为你没有想要它被清除掉;但是你有时会遇到这样的情况,即你忘记那些你真的不需要你的程序的对象,仍然保留在一个结构中。


你的例子:

 Person p1 = new Person("John");
 Person p2 = new Person("Peter");
 p1 = p2;  // p1 = p2 = "John". "Peter" will be GCd.

使用这样的行看Java代码并不罕见:

 Person p1 = new Person("John");
 doSomethingWith(p1);
 p1 = null; // let the GC take back the Person

但是如果你正确地构造代码 - 使用局部变量和简短方法 - 这不应该是必要的,我认为这样做不好。

答案 2 :(得分:0)

不,您的代码没有内存泄漏。

您可以使用visualvm自行分析。

请参阅更多信息:http://www.javaworld.com/article/2072864/heap-dump-and-analysis-with-visualvm.html