JVM--对象已死吗

10 篇文章 0 订阅

概述

在这里插入图片描述

当需要排查各种内存溢出、内存泄漏问题是,当垃圾收集成为系统达到更高并发量的瓶颈时,我们就需要对这些自动化的技术试试必要的监控和调节

程序计数器、虚拟机栈、本地方法栈随线程而生、随线程而灭;栈帧随着方法的开始入栈、方法结束出栈。这几个内存区域的内存分配和回收都具有确定性,在这几个区域内不需要过多考虑回收问题,因为方法结束或者线程结束时,内存自然就跟着回收了。
而对于Java堆和方法区,我们只有在程序运行期间才能知道会创建哪些对象,这部分内存的分配和回收都是动态的,垃圾收集器所关注的就是这部分内存。

判断对象是否存活

在这里插入图片描述
若一个对象不被任何对象或者变量引用,那么他就是无效对象,需要被回收

引用计数算法

在对象头维护者一个counter计数器,对象被引用一次这个计数器就+1;若引用失效则计数器-1。当计数器为0时,就认为该对象无效了。

  • 引用计数算法实现简单,判定效率也高
  • 引用计数算法在主流的Java虚拟机里面不被选用,因为他很难解决对象之间相互循环引用的问题。

可达性分析算法

这个算法的基本思想就是通过一系列的称为“GC Roots”的对象作为起点,从这些节点开始向下搜索,节点所走过的路径称为引用链。
当一个对象到GC Roots没有任何引用链想来你的话,则证明此对象是不可用的

在这里插入图片描述

在Java语言中,可作为GC Roots的对象包括

  • 虚拟机栈(栈帧中的本地变量表)中引用的对象
  • 方法区中类静态属性引用的对象
  • 方法区中常量表中的对象
  • 本地方法栈中JNI(即一般说的Native方法)引用的对象

GC Roots并不包括堆中对象所引用的对象,这样就不会有循环引用的问题

引用的类型

判断对象是否存活都与“引用”有关。
在JDK1.2之前,Java中的引用的定义很传统:如果reference类型的数据中存储的数值代表的是另外一块内存的起始地址,就成这块内存代表着一个引用。再这种顶一下一个对象只有被引用和没有被引用两种状态。过于狭隘。
我们希望能描述这样一类对象:当内存空间还足够时,则能保留在内存之中;如果内存空间在进行垃圾收集后还是很紧张,则可以抛弃这些对象。很多系统的缓存功能都符合这样的应用场景。

在JDK1.2之后,Java对引用的概念进行了扩充,将引用分为4种

  • 强引用(Strong Reference)
  • 软引用(Soft Reference)
  • 弱引用(weak Reference)
  • 虚引用(Phantom Reference)

强引用

类似“Object o = new Object()”这类引用,只要强引用还在,垃圾收集器永远不会回收掉被引用的对象。
但是,如果我们错误地保持了强引用,比如:赋值给了static变量,那么对象在很长一段时间内不会被回收,会产生内存泄漏

软引用

软引用是一种相对强引用弱化一些的引用,可以让对象豁免一些垃圾收集,只有当JVM认为内存不足时,才会试图回收软引用指向的对象。
JVM会确保在抛出OutOfMemorryError之前,清理软引用指向的对象。软引用通常用来实现内存敏感的缓存,如果还有空闲内存,就可以暂时保留缓存,当内存不足时清理掉,这样就可以保证了使用缓存的同时不会耗尽内存。

弱引用

强度比软引用更弱一些,被若引用关联的对象只能深村到下一次垃圾收集发生之前。当垃圾收集器工作时,无论当前内存是否足够,都会回收掉只被若引用关联的对象。

虚引用

虚引用也称幽灵引用或者幻影引用,它是最弱的一种引用关系。一个对象是否有虚引用的新存在,完全不会对其生存时间构成影响,也无法通过虚引用来取得一个对象实例。
它仅仅是提供了一种确保对象被finalize以后,做某些事情的机制,比如,通常用来做所谓的Post-Mortem清理机制如何理解??
为一个对象设置虚引用关联的唯一目的就是能在这个对象被收集器回收时收到一个系统通知

虚引用主要用来跟踪对象被垃圾回收的活动

虚引用与软引用和弱引用的一个区别在于:虚引用必须和引用队列(ReferenceQueue)联合使用。当垃圾回收器粘杯回收一个对象时,如果发现它还有虚引用,就会在回收对象内存之前,把这个虚引用加入到与之关联的引用队列中。程序可以通过判断引用队列中是否已经加入了虚引用来了解被引用的对象是否将要被来及回收。程序如果发现某个虚引用已经被加入到引用队列,那么就可以在所引用的对象的内存被回收之前采取必要的行动采取什么行动???

特别注意,在程序设计中一般很少使用弱引用与虚引用,使用软引用的情况较多,这是因为软引用可以加速JVM对垃圾内存的回收速度,可以维护系统的运行安全,防止内存溢出等问题的产生(如何理解??

对象的生存与死亡

判断是否死亡的两次标记

要真正宣告一个对象死亡至少要经过两次标记过程:如果对象在进行可达性分析后发现没有与GC Roots相连接的引用链,那它将会被第一次标记并且进行一次筛选,筛选的条件是此对象是否有必要执行finalize()方法。
当对象没有覆盖finalize(0方法,或者finalize()方法已经被虚拟机调用过,就会被视为“没有必要执行”。那么对象就被回收了。
如果这个对象被判定为有必要执行finalize()方法,那么这个对象将会被放置在一个叫做F-Queue队列中,并在稍后由一个虚拟机自动建立的、低优先级的Finalizer线程去执行它,但并不保证等待它运行结束(防止方法超市或者出现死循环导致内存回收系统崩溃)。如果finalize()方法出现耗时操作,虚拟机就直接停止指向该方法,将对象清除。

对象重生或死亡

如果在执行finalize()方法时,对象重新与引用链上的任何一个对象建立关联关系,譬如把this赋值给某一个类变量后者对象的成员变量,那在第二次标记时它将会被移除“即将回收”的集合,得到重生;否则被回收。

任何对象的finalize()方法都只会被系统自动调用一次,如果对象面临下一次回收,它的finalize()方法不会被再次执行,因此自救只有一次。

回收方法区

方法区存放生命周期较长的类信息、常量池、静态变量,每次垃圾收集只有少量的来及被清除。

在方法区(永久代)的垃圾回收主要回收两部分内容

  • 废弃常量
  • 无用的类

判定废弃常量

只要常量池中的常量不被任何变量或对象引用,那么这个常量就会被清除掉。

判定无用的类

类需要同时满足下面3个条件才能算是“无用类”

  • 该类所有的实例都已经被回收,也就是Java堆中不存在该类的任何实例
  • 加载该类的ClassLoader已经被回收
    该类对应的java.lang.Class对象没有在任何地方被引用,无法在任何地方通过反射访问该类的方法

一个类被迅即加载进方法区,那么在堆中就会有一个代表该类的对象:“java.lang.Class”。这个对象在类被加载进方法区的时候创建,在方法区该类被删除时清除。

是否对类进行回收,HotSpot虚拟机提供了参数进行控制:-XnoClassgc

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值