JVM 性能调优(二)

对象的访问

建立对象自然是为了使用对象,java 程序需要通过栈上的reference数据来操作对上的对象,但在JAVA虚拟机规范中只规定了一个指定对象的引用,没有指定引用通过何种方式去定位堆中的对象,所以对象的访问方式取决于虚拟机实现,目前主流的访问方式:使用句柄直接指针

  • 使用句柄

    java堆中会划分一块内存来作为句柄池。reference存的是对象的句柄地址。句柄中包含对象实例的指针对象类型的指针

    句柄

  • 直接指针

    如果使用直接指针访问,那么java堆对象的布局就必须考虑如何放置访问类型数据的相关数据。而reference中存储的直接就是对象地址

    直接指针

优缺点:

  • 使用句柄访问的最大好处就是reference中存储的是稳定的句柄地址,在对象被移动(垃圾收集时移动对象是非常普通的行为)时只会改变句柄的实力数据指针,而reference本身不需要修改

  • 使用直接指针最大好处速度更快,节省一次指针定位的时间开销,HotSpot 使用的就是直接指针的方式

垃圾收集算法

判断对象是死是活

垃圾收集器在对堆进行会收前,第一件事情就是要确定这些对象之中哪些海“存活着”,哪些已经“死去”。

引用计数法

给对象添加一个引用计数器,每当有一个地方引用它时,计数器+1,当引用失效时,计数器-1;任何时刻计数器为0的对象就是不能再被使用的

缺点:难以解决对象之间相互循环引用的问题,例如:A.instance=B,B.instance=A

可达性分析

基本思想 通过一系列的称为“GC Roots”的对象作为起点,从这些节点开始向下搜索,搜索所走过的路径称为引用链(reference chain),当一个对象到“GC Roots”没有任何引用链相连,则证明此对象是不可用的,如下图对象 object5/6/7 到GC Roots是不可达的,所以她们将会被判定是可回收的对象

在Java语言中 可作为GC Roots的对象包括以下几种

  1. 虚拟机栈(栈帧中的本地变量表)中引用的对象
  2. 方法区中磊静态属性引用的对象
  3. 犯法区中常量引用的对象
  4. 本地方法栈中JNI 引用的对象

另外 即使在可达性分析算法中不可大的对象,也并非时“非死不可”的,这时候它们暂时处于“缓行”阶段,真正宣告一个对象死亡,至少要尽力两次标记:

  1. 对象在进行可达性分析后发现没有与GC Roots相连接的引用链,则会被第一次标记并进行一次筛选,筛选条件:对象是否有必要执行finalize(),当对象没有覆盖finalize(),或者finalize()已被虚拟机调用过,都将被视为“没有必要执行”
    如果 这个对象被视为有必要执行finalize()方法,那么对象会被放置在一个叫F-Queue的队列中,并在稍后由一个由虚拟机自动建立的、低优先级的Finalize线程去执行它,
    这里所谓的执行,是指虚拟机会触发这个方法,但并不承诺会等待它运行结束,原因(如果一个对象在finalize()中执行缓慢,或者发生了死循环,可能导致F-Queue中的其他对象永久处于等待状态,甚至导致整个内存回收系统崩溃)。

  2. finalize()是对象逃离死亡命运的最后一次机会,稍后GC对于F-Queue中的对象进行第二次小规模标记,如果要拯救自己,只需要重新与引用链上任何一个对象建立关联关系。譬如把this赋值给某个类变量,这样在第二次标记时它将被移除“即将回收”的集合,如果这时候还灭有逃脱,那基本上就真的被回收了

说说引用

jdk1.2以后java对引用进行了扩充,将引用分为:

  • 强引用(strong reference) 无论何时都不会被回收
  • 软引用(soft reference) 用来描述有用但非必需的对象,对于软引用关联的对象,在系统将要发生内存溢出之前,将会把这些对象列进回收范围之中进行第二次回收,如果这次回收还没有足够的内存,才会抛出内存溢出异常
  • 弱引用(weak reference)也是用于描述非必需对象的,强度比软引用更弱一些,被弱引用关联的对象只能生存到下一次垃圾回收发生之前,当垃圾收集器工作时,无论当前内存是否足够,都会被回收
  • 虚引用(phantom reference)最弱的一种关系引用,一个对象是否具有虚引用的存在,完全不会对其生存时间构成影响。也无法通过虚引用来取得一个对象实例,设置虚引用的唯一目的就是能在这个对象被收集器回收时收到一个系统通知。
回收方法区

主要是回收:废弃常量 和 无用的类

无用的类,要同时满足三个条件才能算 “无用的类”

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

参考

  1. 《深入理解Java虚拟机 JVM高级特性与最佳实践》 周志明 著
坚持原创技术分享,您的支持将鼓励我继续创作!