为什么JAVA的垃圾回收机制无法避免内存泄漏

摘要:
然而,在主流的Java虚拟机中没有使用引用计数器算法来管理内存。主要原因是难以解决对象之间的循环引用问题。
一、本文参考:
   1.《深入理解java虚拟机 JVM高级特性与最佳实践》
   2. http://coderevisited.com/memory-leaks-in-java/
 
二、对象已死的判定方法
      要进行JVM中对象回收首先要判断对象是否已经死亡,判断的方法有如下几个:
     1.引用计数法
         给对象中添加一个引用计数器,每当有一个地方引用它时,计数器值就加1;当引用失效时,计数器值就减1;任何时刻 计数器为0的对象就是不可能再被使用的。
 但是主流的java虚拟机里面没有选用引用计数器算法来管理内存,其中最主要的原因是它很难解决对象之间相互循环引用的问题。
 
     2.可达性分析算法
     这个算法的基本思想就是通过一系列的称为“GC Roots"的对象作为起始点,从这些节点开始向下搜索,搜索所走过的路径称为引用链,当一个对象到GC Roots没有任何引用链相连接时,则证明此对象是不可用的。如下图所示,对象object5、object6、object7虽然互相有关联,但是它们到GC Roots是不可达的,所以它们将会被判定为是可回收对象。
 为什么JAVA的垃圾回收机制无法避免内存泄漏第1张
 
三、导致内存泄漏的情况及代码
 
java 堆内存泄漏。是由于java对象不停创建但是没有释放对象引用导致的。
以下是关于java代码,此代码是引自http://coderevisited.com/memory-leaks-in-java/
 
类com.code.revisited.memoryleaks.Stack提供了实现栈的一些方法,包括遍历,入栈,出栈等操作。假设原来目的是为了现实使用(当然这里是为了解释内存泄漏)。
 
package com.code.revisited.memoryleaks;

import java.util.Iterator;
import java.util.NoSuchElementException;

/**
 * @author sureshsajja
 * 
 */
public class Stack<E> implements Iterable<E> {

    private int N;
    private E[] array;

    @SuppressWarnings("unchecked")
    public Stack(int capacity) {
        array = (E[]) new Object[capacity];
    }

    @Override
    public Iterator<E> iterator() {
        return new StackIterator();
    }

    private class StackIterator implements Iterator<E> {

        private int i = N - 1;

        @Override
        public boolean hasNext() {
            return i >= 0;
        }

        @Override
        public E next() {
            if (!hasNext()) {
                throw new NoSuchElementException();
            }
            return array[i--];
        }

        @Override
        public void remove() {
            throw new UnsupportedOperationException();

        }

    }

    public void push(E item) {
        if (isFull()) {
            throw new RuntimeException("Stack overflow");
        }
        array[N++] = item;
    }

    public E pop() {
        if (isEmpty())
            throw new RuntimeException("Stack underflow");
        E item = array[--N];
        return item;
    }

    public boolean isEmpty() {
        return N == 0;
    }

    public int size() {
        return N;
    }

    public boolean isFull() {
        return N == array.length;
    }

    public E peek() {
        if (isEmpty())
            throw new RuntimeException("Stack underflow");
        return array[N - 1];
    }

}

类com.code.revisited.memoryleaks.StackTest用于执行栈操作。要进行入栈及出栈10000次操作,理想是入栈时分配堆内存,出栈后对象被回收。

package com.code.revisited.memoryleaks;

/**
 * @author  sureshsajja
 *
 */
public class StackTest {

        /**
        * @param args
        */
        public static void main(String[] args) {
              Stack<Integer> s = new Stack<Integer>(10000);
               for (int i = 0; i < 10000; i++) {
                     s.push(i);
              }

               while (!s.isEmpty()) {
                     s.pop();
              }
               while (true ) {
                      // do something
              }

       }

}
执行开始。我们使用VisualVM进行观察。为了更明显一些,将栈操作部分代码注释也执行一下。
package com.code.revisited.memoryleaks;

/**
 * @author  sureshsajja
 *
 */
public class StackTest {

        /**
        * @param args
        */
        public static void main(String[] args) {
//            Stack<Integer> s = new Stack<Integer>(10000);
//            for ( int i = 0; i < 10000; i++) {
//                   s.push(i);
//            }
//
//            while (!s.isEmpty()) {
//                   s.pop();
//            }
               while (true ) {
                      // do something
              }

       }

}

把栈操作的设为1号,没有栈操作的设置为2号,分别生成Heap Dump文件,我们看一下类实例的截图:

首先是1号截图
为什么JAVA的垃圾回收机制无法避免内存泄漏第2张
首先是2号截图
为什么JAVA的垃圾回收机制无法避免内存泄漏第3张
显然预期的栈操作出栈后并没有释放掉Integer对象的引用(实际上看代码也知道),所以不会被GC回收。真正的实际情况这种引用将会很隐蔽,但是根本总是由于对象仍然被引用。
  
四、结语
  本篇仅对java堆内存泄漏进行了简单说明,下一篇将讨论其他相关的内存泄漏。有不对的地方欢迎拍砖>_<

免责声明:文章转载自《为什么JAVA的垃圾回收机制无法避免内存泄漏》仅用于学习参考。如对内容有疑问,请及时联系本站处理。

上篇零配置文件搭建SpringMVC实践纪录SSAS:OLE DB 错误: OLE DB 或 ODBC 错误 : Login failed for user 'NT ServiceMSSQLServerOLAPService'下篇

宿迁高防,2C2G15M,22元/月;香港BGP,2C5G5M,25元/月 雨云优惠码:MjYwNzM=

相关文章

指针与内存泄露 mark

摘自http://www.quanxue.cn/JC_CLanguage/CLang/Clang13.html C语言程序员要严防内存泄漏,这个“内存泄漏”就是由动态内存分配引起的。指针是C语言和其它语言的最大区别,也是很多人不能跨入C语言的一道门槛。既然指针是这么一个“危险”的坏东西,干吗不取消它呢? 其实指针本身并没有好坏,它只是一种操作地址的方法,学会...

您知道吗:未释放事件Handler可能导致内存泄漏

以前曾看见过这样一个问题:托管代码会不会导致内存泄漏。自己对GC的了解也不是很深,但还是比较赞成这样的观点:托管代码不会产生内存泄漏,除非你没有正确释放非托管资源。今天看到一个非常有趣的例子,关于没有释放事件的Handler导致的内存泄漏。以前对于释放Handler的观念是一点也没有,这主要因为没此方面的意识,没有养成好的习惯。只知道当关心这个事件的时候就注...

使用Jconsole监控weblogic的配置方法

在项目中发现full gc非常频繁。达到了每分钟13次。我怀疑可能会有内存泄露。于是在晚上找了内存泄露的资料。 内存长期占用并导致系统不稳定一般有两种可能: 1. 对象被大量创建而且被缓存,在旧的对象释放前又有大量新的对象被创建使得内存长期高位占用。 表现为:内存不断被消耗、在高位时也很难回归到低位,有大量的对象在不断的创建,经过很长时间后又被回收。例如:...

【进阶1-4期】JavaScript深入之带你走进内存机制(转)

这是我在公众号(高级前端进阶)看到的文章,现在做笔记 https://mp.weixin.qq.com/s/yK4DPKhkmkiroasWJMrJcw 阅读笔记 JS内存空间分为栈(stack)、堆(heap)、池(一般也会归类为栈中)。 其中栈存放变量,堆存放复杂对象,池存放常量,所以也叫常量池。 昨天文章介绍了堆和栈,小结一下,【进阶1-3期】...

使用Xcode Instruments定位APP稳定性问题

XcodeInstruments提供了各种各样的工具用来定位APP的各种稳定性问题。这里简单总结几个问题: 1.内存泄漏 Xcode->Open Developer Tools->Instruments->Leak,打开后点击运行。执行测试流程后,如果没有内存泄漏,则Leak工具显示的是√,如果有泄漏,则显示×。 这时候选中×的区域,...

Android开发技术周报 Issue#47

教程 Android内存泄露案例分析 一款优秀的Android应用,不仅要有完善的功能,也要有良好的体验,而性能是影响体验的一个重要因素。内存泄露是Android开发中常见的性能问题。这篇文章,通过我们曾经遇到的一个真实的案例,来讲述一个内存泄露问题,从发现到分析定位,再到最终解决的全过程。 Android微信上的SVG 面对android的各种dp...