zoukankan      html  css  js  c++  java
  • 理解JVM之对象的生命周期

    1.对象的创建

      1) 当虚拟机遇到一条new的指令时,首先将去检查这个指令的参数是否能在常量池中定位到一个类的符号引用,并且检查这个符号引用代表的类是否已被加载,解析和初始化过.

      2) 在类加载检查通过后,接下来的虚拟机将为新生对象分配内存.

      3) 内存分配完成后,虚拟机需要将分配到的空间内存初始化为默认值.

      4) 接下来,虚拟机对对象进行有必要的设置,例如这个对象是哪个类的实例,如何才能找到这个类的元数据信息,对象的哈希码,对象的GC分代年龄等信息,这些信息存放在对象头.

      5) 以上工作结束后,从虚拟机角度看,对象已经创建,接下来是对对象的字段按照程序员的意思进行初始化.

    2.对象的访问

      java程序需要通过栈上面的reference数据来操作堆上的具体对象.

      目前访问对象的方式有两种,一种使用句柄另一种使用直接指针:

      1) 使用句柄的话,在java堆中会划分出一块内存作为句柄池,reference中存储的就是对象的句柄地址,句柄中包含了对象的实例数据与类型数据格子的具体地址信息,这里的reference可以类比成c语言的二重指针.

      2) 使用直接指针的话,那么java堆对象的内存布局就必须考虑如何放置访问类型数据的相关信息,reference存放的就是对象的直接地址,可以类比c语言的一重指针..

      两种方法各有优劣,由于对象在堆中被移动是非常普遍的现象,前者可以只修改句柄中实例数据指针的信息,不需要修改reference的值但是它需要进行两次指针定位.后者只需要一次指针定位,速度快但是对象移动时需要修改reference的值.

    3.对象的死亡

      在java中不需要程序员来管理内存,那么,我们程序中使用的实例对象在分配了空间之后何时死亡,何时回收他的内存呢?

      这里有一个很经典的算法叫做引用计数法.该算法是给对象添加一个引用计数器,每当一个地方引用便对它加一,当一个引用失效便对它减一.当计数器为0时便回收它.这个算法实现简单,判断效率高,大多数情况下是一个不错的算法,但是会这个算法无法解决两个对象相互引用的情况,比如对象A和对象B,A和B中有对方的引用,那么当其他地方的引用全部都失效之后,只剩下这两个对象的相互引用,这种情况对象AB是不会被回收的,有内存泄露的风险.

      另一个算法是可达性分析算法:这个算法是通过一系列成为"GC Roots"的对象为起点,从这些节点向下搜索,经过的路径叫做引用链,当一个对象到GC Roots没有任何引用链存在,则该对象是不可用的,应该回收.

      在java中,GC Roots对象包括:虚拟机栈(栈帧中的本地变量表)中的引用对象,方法区中类静态属性引用的对象,方法区中常量引用的对象,本地方法栈中JNI(即Native方法)引用的对象.

    4.再谈引用

      从上面看到,对象是否死亡都与引用有关.JDK 1.2之后,java对引用进行了扩充:

      1) 强引用:程序代码中普遍存在的,例如"Object obj=new Object()"这类的引用,只要强引用还在,对象就不会被回收

      2) 软引用:用来描述一些非必须的但还有用的对象,软引用关联的对象,在系统将要发生内存溢出异常之前,将会把这些对象列进对象回收范围之中进行第二次回收,软引用类SoftReference.

      3) 弱引用:用来描述费必须的对象.被弱引用关联的对象会在下一次垃圾收集被回收.弱引用类WeakReference.

      4) 虚引用:虚引用完全不会对对象的生存时间构成影响,也无法通过虚引用获取对象实例.为对象设置虚引用的目的只是为了在这个对象被回收时收到一个通知.虚引用类PhantomReference.

    5.生存还是死亡

      即使在可发性分析算法中不可达的对象,也不一定是死刑而是死缓.要宣告一个对象死亡,要经过至少两次标记过程:如果对象经过可达性分析后发现没有与GC Roots相连的引用链,那它会被第一次标记,并且进行一次筛选,筛选条件是此对象是否有必要执行finalzie()方法.当对象米有覆盖finalize()方法或者finalize()方法已经被虚拟机调用过,虚拟机都会视为没有必要执行.

      如果一个对象有必要执行finalize()方法,则会把这个对象放到一个F-Queue队列之中,并在稍后由虚拟机自动建立一个低优先级的Finalizer线程去执行它.这个执行是指虚拟机会触发那个方法,但是不保证会等他运行结束后.例如当主线程已经结束,Finalizer线程还未执行完,那么Finalizer将结束,不会继续执行.当Finalizer线程执行完之后,胡对F-Queue队列中的对象进行第二次标记,如果对象在finalize()方法中拯救了自己,重新与引用链上的任何一个对象建立关联(例如将自己赋给一个静态变量),第二次标记就会把它移除即将回收的集合.如果第二次依然被标记为回收,那么对象就真的会被回收.

      finalize()方法并不是C/C++中的析构函数,他运行代价高,不确定性大,无法保证各个对象的调用顺序.尽量不要使用finalize()方法.

  • 相关阅读:
    回归测试
    系统测试
    单元测试
    软件测试规律之木桶原理
    集成测试
    软件测试度量
    测试用例设计方法之错误推测法
    测试用例设计方法之因果图方法
    有趣的算法:1元=1分
    【转】 arcServer 一些 FAQ
  • 原文地址:https://www.cnblogs.com/ouhaitao/p/8580665.html
Copyright © 2011-2022 走看看