java语言

Java内存回收

时间:2024-07-17 12:05:59 java语言 我要投稿
  • 相关推荐

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