深入理解java虚拟机学习-3

文章详细介绍了Java虚拟机中的垃圾收集机制,包括垃圾收集的三个问题:哪些内存需要回收、何时回收以及如何回收。讨论了引用计数法和可达性算法两种主要的垃圾检测策略,以及它们各自的优缺点。此外,还提到了方法区的回收、Java中的四种引用类型(强引用、软引用、弱引用、虚引用)以及不同垃圾收集算法,如标记清除、标记复制和标记整理。最后,文章介绍了分代收集理论在现代Java虚拟机中的应用。
摘要由CSDN通过智能技术生成

垃圾收集器

垃圾收集,即GC操作。当一些对象不在使用的时候,java虚拟机就需要将这些对象的内存数据給回收掉。那么实际上,垃圾回收需要解决三个问题。

  • 哪些内存需要回收?
  • 什么时候回收内存?
  • 如何回收这些内存??

引用计数法

引用计数法是一个较为古老的算法,常规来说,对任意一个对象,可以添加一个引用计数器,如果这个对象被引用,那么对应的引用计数器需要加一,如果一个对象没有任何对象引用。 那么下一次垃圾收集的时候,就可以将这个对象的内存回收。实际上,这个方法也存在一些问题,如果两个对象发生循环引用,即A引用B,B引用A,那么这两个对象的引用计数得到的结果始终至少为1。 那么对于这种问题就是比较难以处理的。因此,大多数的时候引用计数法都不会被使用。 笔者认为,引用计数法,一定程度上是空间换时间的算法,在一定程度上,这种方法的速度比较快。原理简单,使用起来比较快捷。

可达性算法

大多数能做内存管理的语音,一般都是使用可达性算法用于作为内存管理的。这个算法的基本思路就是通过 一系列称为“GC Roots”的根对象作为起始节点集,从这些节点开始,根据引用关系向下搜索,搜索过 程所走过的路径称为“引用链”(Reference Chain),如果某个对象到GC Roots间没有任何引用链相连, 或者用图论的话来说就是从GC Roots到这个对象不可达时,则证明此对象是不可能再被使用的。1
利用可达性分析算法判定对象是否可回收
如图所示,从GCRoots出发,object1,object,object3,object4可以到达,所以在GC之后对象依然可以存活,而对象object5,object6,object7不可以抵达,所以这些对象之后就会被垃圾回收掉。

哪些对象可以被用于GC Roots呢?
·在虚拟机栈(栈帧中的本地变量表)中引用的对象,譬如各个线程被调用的方法堆栈中使用到的 参数、局部变量、临时变量等。
·在方法区中类静态属性引用的对象,譬如Java类的引用类型静态变量。
·在方法区中常量引用的对象,譬如字符串常量池(String Table)里的引用。在本地方法栈中JNI(即通常所说的Native方法)引用的对象。
·Java虚拟机内部的引用,如基本数据类型对应的Class对象,一些常驻的异常对象(比如
NullPointExcepiton、OutOfMemoryError)等,还有系统类加载器。
·所有被同步锁(synchronized关键字)持有的对象。
·反映Java虚拟机内部情况的JM XBean、JVM TI中注册的回调、本地代码缓存等。
.用户自定义的GC ROOT。

这种方式确定能够解决对对象做垃圾回收的功能,但是这也引入了其他的问题:

  • 由于每GC一次就需要做一次遍历,那么是不是GC的时候,会占用用户执行程序的时间,导致用户使用时出现卡顿现象。
  • 如果是当GC root 遍历对象的时候,又有新的对象加进来了,那么这时候又如何处理这种对象呢?

方法区的回收
方法区的垃圾收集主要回收两部分内容:废弃的常量和不再使用的类型。
由于方法区的回收一般需要很严格的判断,一般其回收的效率不高。

引用详解

在java1.2 之前,java中的reference实际上就是一个指向内存的地址。 老实说,如果是C/C++,实际上笔者认为这个定义已经足够了,因为C/C++里面内存会自己清理,所以这些指针是否为空,一般程序员本身是清楚的。但是在java1.2之后,Java对引用的概念进行了扩充,将引用分为强引用(Strongly Re-ference)、软 引用(Soft Reference)、弱引用(Weak Reference)和虚引用(Phantom Reference)4种,这4种引用强 度依次逐渐减弱。

  • 强引用是最传统的“引用”的定义,是指在程序代码之中普遍存在的引用赋值,即类似“Object obj=new Object()”这种引用关系。无论任何情况下,只要强引用关系还存在,垃圾收集器就永远不会回 收掉被引用的对象。
  • 软引用是用来描述一些还有用,但非必须的对象。只被软引用关联着的对象,在系统将要发生内 存溢出异常前,会把这些对象列进回收范围之中进行第二次回收,如果这次回收还没有足够的内存, 才会抛出内存溢出异常。在JDK 1.2版之后提供了SoftReference类来实现软引用。
  • 弱引用也是用来描述那些非必须对象,但是它的强度比软引用更弱一些,被弱引用关联的对象只 能生存到下一次垃圾收集发生为止。当垃圾收集器开始工作,无论当前内存是否足够,都会回收掉只 被弱引用关联的对象。在JDK 1.2版之后提供了WeakReference类来实现弱引用。
  • 虚引用也称为“幽灵引用”或者“幻影引用”,它是最弱的一种引用关系。一个对象是否有虚引用的 存在,完全不会对其生存时间构成影响,也无法通过虚引用来取得一个对象实例。为一个对象设置虚 引用关联的唯一目的只是为了能在这个对象被收集器回收时收到一个系统通知。在JDK 1.2版之后提供 了Phant omReference类来实现虚引用。

可达性算法没有遍历到对象,对象会立即死亡吗?
即使在可达性分析算法中判定为不可达的对象,也不是“非死不可”的,这时候它们暂时还处于“缓 刑”阶段,要真正宣告一个对象死亡,至少要经历两次标记过程:如果对象在进行可达性分析后发现没 有与GC Roots相连接的引用链,那它将会被第一次标记,随后进行一次筛选,筛选的条件是此对象是 否有必要执行finaliz e()方法。假如对象没有覆盖finaliz e()方法,或者finaliz e()方法已经被虚拟机调用 过,那么虚拟机将这两种情况都视为“没有必要执行”。

垃圾回收算法

当前商业虚拟机的垃圾收集器,大多数都遵循了“分代收集”(Generational Collection)[1]的理论进 行设计,分代收集名为理论,实质是一套符合大多数程序运行实际情况的经验法则,它建立在两个分 代假说之上:

1). 弱分代假说(Weak Generational Hypothesis):绝大多数对象都是朝生夕灭的。
2). 强分代假说(Strong Generational Hypothesis):熬过越多次垃圾收集过程的对象就越难以消亡。

这两个分代假说共同奠定了多款常用的垃圾收集器的一致的设计原则:收集器应该将Java堆划分 出不同的区域,然后将回收对象依据其年龄(年龄即对象熬过垃圾收集过程的次数)分配到不同的区 域之中存储。

确实在java 虚拟机中对java堆做了划分:新生代(其中包括eden, Survivor1,Survivor2),老年代(old)。那么因此对应不同的区域有着不同的GC方式,主要有以下几种:

  • 部分收集(Partial GC):指目标不是完整收集整个Java堆的垃圾收集,其中又分为:
  • 新生代收集(M inor GC/Young GC):指目标只是新生代的垃圾收集。
  • 老年代收集(Major GC/Old GC):指目标只是老年代的垃圾收集。目前只有CMS收集器会有单 独收集老年代的行为。
  • 混合收集(Mixed GC):指目标是收集整个新生代以及部分老年代的垃圾收集。目前只有G1收 集器会有这种行为。
  • 整堆收集(Full GC):收集整个Java堆和方法区的垃圾收集。

以上是GC的方式,那么对应的GC方式一般使用什么算法对内存做清除呢?

标记清除算法

标记清除算法
如图,标记清除算法比较暴力,内存的空间中分为存活对象,可回收对象和未使用的对象。 在一次扫描之后,可回收的对象就直接被清除了。这种方式一般来说回收速度很快。但是后面对象创建的时候,查找未使用的空间就会变得很慢。记得之前java虚拟键创建对象的时候,在堆中申请空间的时候,一把会用一致指针指向已经被使用的地址,然后申请空间,就相当于指针平移一次。由于清除算法,空间中存在很多的缝隙。所以对于小对象申请空间不是特别快捷,大对象申请空间可能空间不够,导致再一次产生垃圾回收。

标记复制算法

标记复制算法
标记复制算法,将空间分成from和to两个区域,当发生垃圾回收时候,就会把可以回收的数据給回收之后。然后将from的剩余数据复制到to区域下,然后调换from和to的区域。这样任何待回收的数据都会在from区域中,to区域始终保持为全新的状态。 这种方式耗时比标记清除算法慢一些。但是代价其实就是空间利用率太低了。 实际上,在后续优化的方式,便是将年轻代分为eden和survivor from和survivor to区域,eden:survivor=8:1。

标记整理算法

标记整理方法
其中的标记过程仍然与“标记-清除”算法一样,但后续步骤不是直接对可 回收对象进行清理,而是让所有存活的对象都向内存空间一端移动,然后直接清理掉边界以外的内存。如果移动存活对象,尤其是在老年代这种每次回收都有大量对象存活区域,移动存活对象并更新。所有引用这些对象的地方将会是一种极为负重的操作,而且这种对象移动操作必须全程暂停用户应用 程序才能进行[1],这就更加让使用者不得不小心翼翼地权衡其弊端了,像这样的停顿被最初的虚拟机设计者形象地描述为“Stop The World”(时间停止吧)。


  1. 深入理解Java虚拟机 第三版 周志明 ↩︎

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值