- 相关推荐
Java内存回收
Java的GC机制是自动进行的,和C语言有些区别需要程序员自己保证内存的使用和回收。下面是小编分享的具体介绍,一起来看一下吧。
Java的内存分配和回收也主要在Java的堆上进行的,Java的堆中存储了大量的对象实例,所以Java的堆也叫GC堆。
下面主要说一下对于java堆的内存回收 。
什么样的内存可以回收
判断法1:引用计数
方法:每有一个引用指向这个对象,那么这个对象的引用计数+1,反之,每有一个引用改变了指向,那么他原来指向的对象引用计数-1,当引用计数为0的时候,这个对象也就不可能被使用了那么就可以被回收了
问题:可能会出现环状的引用,导致不可能被使用的对象永远不可能被回收
示例:
Class A {
A a;
Public static void main(String[] args){
A gc1 = new A();
A gc2 = new A();
Gc1.a = gc2;
Gc2.a = gc1;
Gc1= null;
Gc2 = null;
}
Gc1和gc2都被设置成null了,他们都应该被清理,但是因为gc1的a对象指向gc2,gc2的a对象指向gc1,导致他们的引用计数永远为1,但是他们都永远不可能被使用了,所以这种方法存在漏洞
判断2:可达性分析算法
方法:从一个叫做GC ROOTS的节点出发,所有能够到达的引用对象标记起来,直到走到完全没有引用的地方为止,这样从这个节点连起来的所有的点(引用链),构成的路线就是不可回收的,那么所有没有被到达过的对象均可以被回收
什么可以做GC ROOTS:虚拟机栈(栈帧中的本地变量表)中的引用的对象、方法区中类静态属性引用的对象、方法区中常量引用的对象、本地方法栈中JNI引用的对象
这些对象的特点:不可变并且随时可能被用到,生命周期长
补充:可达性分析的算法,那么没有在引用链上的对象都一定会被清理吗?不一定。当运行可达性分析的算法之后,会对所有没有在引用链上的对象进行一次标记和筛选,筛选的条件为:该对象覆盖了finallize()方法(这个方法是GC的时候如果这个对象要被回收则执行的方法,但是在Thinking in java中不推荐用来处理收尾工作),并且这个方法没有被执行过,那么就会把这个对象放到一个低优先级队列中执行,也就是这个对象的最后抢救的机会,如果这个时候这个对象把自己和在引用链上的引用连了起来,那么他在执行完finallize方法之后,再次判断时就不会被清理,否则会在第二次可达性判断的时候直接清理(因为finallize已经执行过一次了),如果没有覆盖这个方法,那么对不起,再见
回收算法介绍:
回收算法1:标记清理(Mark——sweep)算法
标记所有需要回收对象,然后将他们清理回收
问题:会产生内存碎片
优点:不需要暂停所有线程(Stop the world)
回收算法2:复制
标记后,将所有不需要回收的对象全部复制到一个空的内存中,然后清理刚刚使用的内存块
问题:浪费资源,会有一些内存堆无法被使用
解决:用在新生代,新生代会有80%以上的对象经过一次GC就会死亡,因此采用Eden + Survivor * 2的办法,Eden = 8 * Survivor大小(HotSpot默认),那么每次使用一个Eden + 一个Survivor,然后进行复制清理的时候,清理Eden + Survivor中,然后将可用的对象复制到空闲的Survivor中,然后全部清空前面的使用区,然后使用Eden 和复制到的Survivor
又一个问题:如果Survivor不够怎么办?向老年代借空间,叫做分配担保,不够存放的对象会通过分配担保进入老年代
问题:需要 stop the world
回收算法3:标记整理(Mark——compact)
标记后,将可用内存向一侧摆放,然后清理掉可用内存边缘外部的所有内存区域
优点:没有内存碎片的问题
问题:需要stop the world
回收算法4:分代收集
将堆分代(老年代、新生代),老年代采用标记整理、标记清理等方法,新生代采用复制方法。
为什么:因为老年代大部分对象是可用的,因此如果采用复制算法,虽然没有内存碎片,但是空间浪费大,而且大部分对象没有变化,而在新生代使用复制算法,可以牺牲很小的内存空间就获得较好的效率
HotSpot中内存回收算法
枚举根节点(GC ROOTs)
Java虚拟机采用准确式GC,有一个OOPmap来标记哪个位置有个对象,这样在查找引用链的时候可以较快的找齐所有的引用链
Safepoint
在OOPmap的协助下,这个可以快速且准确的完成枚举,但是问题就是导致这个oopmap变化的指令非常多,如果为每一条指令生成oopmap,那么会需要大量额外空间,因此采用在特定点的地方生成,这些点同时也是safepoint的点,那么当需要枚举根节点的时候,就让线程运行到这个地方再停止。
一种方法:先停止所有线程,然后再让没有到安全点的线程自己跑到安全点再停下来,基本不适用,抢先试中断
另一种方法:主动式中断,设置一个标记,在执行的时候去轮询,而需要中断的时候,直接将这个位置的内存设置不可达,那么线程就会进入一个自陷异常,就自己会中断
【Java内存回收】相关文章:
Java的内存模型11-28
Java编程节省内存的方法12-16
JAVA对象创造及内存布局介绍12-04
如何解决java内存泄漏的问题03-03
查看java对象所占的内存大小的方法12-04
Java编程节省内存的方法有哪些11-21
Windows内存诊断07-07
2g内存的台式机怎么添加内存12-04
电脑内存怎么清理11-30