JVM之垃圾收集与内存分配策略

一、概述

  设计垃圾收集器,需要考虑以下三件事:

  (1)哪些内存需要回收

  (2)什么时候需要回收

  (3)如何回收

  Java内存运行时区域的各个部分分别为:程序计数器,Java虚拟机栈,本地方法栈,Java堆,方法区其中程序计数器,虚拟机栈,本地方法栈3个区域随线程而生,随线程而亡,栈中的栈帧随方法的进入和退出而有条不絮地执行着出栈和入栈操作。每一个栈帧中分配多少内存基本上是在类结构确定下来就已知的。这几个区域的内存分配和回收都具备确定性。

  Java堆和方法区这两个区域则有着显著的不确定性:一个接口的多个实现类需要的内存可能会不一样,一个方法所执行的不同分支所需要的内存可能也不一样,只有在运行期间,我们才能知道程序究竟会创建哪些对象,创建多少对象,这部分内存的分配和回收是动态的,垃圾收集器所关注的正是这部分内存该如何管理。

二、确定哪些内存需要回收?

(1)引用计数法判断对象的状态(死or活)

  引用计数法判断存活对象:在对象中添加一个引用计数器,每当有一个地方引用它时,计数器值就加1,当引用失效时,计数器值就减一;任何时刻计数器为0的对象是不可能再被使用的。引用计数法虽然占用了一些额外的内存空间来进行计数,但是它原理简单,判定效率高。但是无法解决循环引用的问题。

  A=B,B=A,此时A,B互相引用对方,导致它们的引用计数都不为0,引用计数法也就无法回收它们。

(2)可达性分析算法判断对象是否存活?

  此算法的基本思路通过一系列称为“GC Roots”的根对象为起始节点集,从这些节点开始根据引用关系向下搜索,搜索过程所走的路径称为“引用链”,如果某个对象到GC Roots没有任何引用链相连,则说明此对象是不可能再被引用的。

  GC Roots集合为:

  1)虚拟机栈中所引用的对象,如何局部变量,临时变量。

  2)方法区中静态属性引用的对象,如类变量。

  3)方法区中常量引用的对象,如字符串常量池里的引用。

  4)虚拟机内部的引用,如基本数据类型对应的Class对象。

  5)所有被同步锁(synchronized)持有的对象。

  注意:用户所选的垃圾收集器以及当前回收的内存区域不同,所以某个区域里的对象完全有可能被堆中其他区域的对象所引用,此时需要将这些关联区域的对象也加入到GC Roots中。

(3)引用的类型

  1)强应用:类似于Object obj=new Object(),这样的引用关系无论任何情况,垃圾收集器永远不会回收掉这样被引用的对象。

  2)软引用:一些有用,但不是必须的对象。只被软引用关联的对象,在系统将要发生内存溢出异常前,会把这些对象列进回收范围之中进行第二次回收,如果这次回收还没有足够的内存,才会抛出内存溢出异常。

  3)弱引用:被弱引用关联的对象只能生存到下一次垃圾收集发生为止。当垃圾收集器开始工作,无论当前内存是否足够,都会回收掉只被弱引用关联的对象。

  4)虚引用:无法通过虚引用来取得一个对象实例,为一个对象设置虚引用关联的唯一目的只是为了能在这个对象被收集器回收时收到一个系统通知。来监听重要对象的回收;可以通过虚引用来判断gc的频率,如果频率过大,内存使用可能存在问题,才导致了系统gc频繁调用。

  

猜你喜欢

转载自www.cnblogs.com/hdc520/p/12540063.html