GC介绍
在java中垃圾回收是自动释放的,不像c/c++需要手动释放,手动释放可能处理不当,
会造成内存泄漏与数据混乱等等影响。
GC常用的回收算法
1,计数
引用计数是垃圾收集器中的早期策略。在这种方法中,堆中每个对象(不是引用)都有一个引用计数。
当一个对象被创建时,且将该对象分配给一个变量,该变量计数设置为1。
当任何其它变量被赋值为这个对象的引用时,计数加1(a = b,则b引用的对象+1),
但当一个对象的某个引用超过了生命周期或者被设置为一个新值时,对象的引用计数减1。
任何引用计数为0的对象可以被当作垃圾收集。当一个对象被垃圾收集时,它引用的任何对象计数减1。
优点:引用计数收集器可以很快的执行,交织在程序运行中。对程序不被长时间打断的实时环境比较有利。
缺点: 无法检测出循环引用。如父对象有一个对子对象的引用,子对象反过来引用父对象。
这样,他们的引用计数永远不可能为0.
2,标记跟踪
现在大多数JVM采用对象引用遍历。对象引用遍历从一组对象开始,沿着整个对象图上的每条链接,
递归确定可到达(reachable)的对象。如果某对象不能从这些根对象的一个(至少一个)到达,
则将它作为垃圾收集。在对象遍历阶段,GC必须记住哪些对象可以到达,以便删除不可到达的对象,
这称为标记(marking)对象。
下一步,GC要删除不可到达的对象。删除时,有些GC只是简单的扫描堆栈,
删除未标记的未标记的对象,并释放它们的内存以生成新的对象,这叫做清除(sweeping)。
这种方法的问题在于内存会分成好多小段,而它们不足以用于新的对象,但是组合起来却很大。
因此,许多GC可以重新组织内存中的对象,并进行压缩(compact),形成可利用的空间。
为此,GC需要停止其他的活动活动。这种方法意味着所有与应用程序相关的工作停止,
只有GC运行。结果,在响应期间增减了许多混杂请求。
另外,更复杂的 GC不断增加或同时运行以减少或者清除应用程序的中断。
有的GC使用单线程完成这项工作,有的则采用多线程以增加效率。
3,在Java中还有很多收集器的算法,copiny,标记压缩,增量等等。
GC回收过程
垃圾回收线程会从“根集(Root Set)”开始进行对象引用的遍历。
所谓的“根集”,就是正在运行的线程中,可以访问的引用变量 的 集合(比如所有线程当前函数的参数和局部变量、
当前类的成员变量等等)。
垃圾回收线程先找出被根集直接引用的所有对象(不妨叫集合1),
然后再找出被集合 1直接引用的所有对象(不妨叫集合2),
然后再找出被集合2直接引用的所有对象......如此循环往复,直到把能遍历到的对象都遍历完。
凡是从根集通过上述遍历可以到达的对象,都称为可达对象或有效对象;
反之,则是不可达对象或失效对象(也就是垃圾)。
凡是从根集通过上述遍历可以到达的对象,都称为可达对象或有效对象;
反之,则是不可达对象或失效对象(也就是垃圾)。
GC回收触法原因
- GC_CONCURRENT :当堆内存增长到一定程度时会触发。此时触发可以对堆中的没有用的对象及时进行回收,腾出空间供新的对象申请,避免进行不必要的增大堆内存的操作。
- GC_EXPLICIT :当程序中调用System.gc()方法触发。这个方法应避免出现在程序中调用。因为JVM有足够的能力来控制垃圾回收。
- GC_EXTERNAL_MALLOC :当Bitmap和NIO Direct ByteBuffer对象分配外部存储(机器内存,非Dalvik堆内存)触发。这个日志只有在2.3之前存在,从2.3系统开始,垃圾回收进行了调整,前面的对象都会存储到Dalivik堆内存中。所以在2.3系统之后,你就再也不会看到这种信息了。
- GC_FOR_MALLOC :当堆内存已满,系统需要更多内存的时候触发。这条日志出现后意味着JVM要暂停你的程序进行垃圾回收操作。
- GC_HPROF_DUMP_HEAP :当创建一个内存分析文件HPROF时触发。
总结:既然GC去对内存进行回收为什么会有内存泄漏?
内存泄漏
内存泄漏也称作“存储渗漏”,用动态存储分配函数动态开辟的空间,
在使用完毕后,GC并没有进行对它回收(注意:GC是不会回收直接或间接地引用到gc roots的对象),
导致未释放,结果导致一直占据该内存单元。直到程序结束。
(其实说白了就是该内存空间使用完毕之后未回/收)即所谓内存泄漏。
补充:Java内存泄漏指的是进程中某些对象(垃圾对象)已经没有使用价值了,
但是它们却可以直接或间接地引用到gc roots导致无法被GC回收。无用的对象占据着内存空间,
使得实际可使用内存变小,形象地说法就是内存泄漏。
内存溢出
内存溢出(out of memory)通俗理解就是内存不够,通常在运行大型软件或游戏时,
软件或游戏所需要的内存远远超出了你主机内安装的内存所承受大小,就叫内存溢出。
此时软件或游戏就运行不了,系统会提示内存溢出,有时候会自动关闭软件。
Android的虚拟机是基于寄存器的Dalvik,它的最大堆大小一般是16M,有的机器为24M。
因此我们所能利用的内存空间是有限的。如果我们的内存占用超过了一定的水平就会出现OutOfMemory的错误。
参照以下各位老兄的幸苦劳动: