28 关于 Finalizer

18 篇文章 0 订阅

前言

// 呵呵 03.12加班, 是一件无聊的事情

接前面几篇

25 关于 Signal Dispatcher

26 关于 Attach Listener

27 关于 Reference Handler

呵呵 关于常见的几个线程AttchListener,Signal Dispatcher, Reference Handler, Finalizer, 虽然 平时使用的不是很多吧, 但是 还是可以了解一下的

这里 我们便来看一下Finalizer这个线程吧

这个是 对象被 gc回收 处理之后, 需要回调的相关方法, 用类似于 c++ 里面的析构函数, 不过这里面可以有一些骚操作

一下的相关代码, 截图如果没有特殊说明基于 jdk9

测试代码

package com.hx.test06;

import static com.hx.test05.Test20DefNewGc.touchMinorGc;

/**
 * Test03Finalizer
 *
 * @author Jerry.X.He <970655147@qq.com>
 * @version 1.0
 * @date 2020-05-17 09:31
 */
public class Test03Finalizer {

  // identStr
  private String identStr = "identStr";
  int f01;
  int f02;
  int f03;
  int f04;
  int f05;

  // Test03Finalizer
  // vmOpts : -Xint -server -Xmx600m -Xms600m -XX:PermSize=128M -XX:MaxPermSize=128M -XX:NewSize=128m -XX:MaxNewSize=128m -XX:SurvivorRatio=8 -XX:+UseSerialGC -XX:+PrintGCDetails
  public static void main(String[] args) {

    Test03Finalizer obj = new Test03Finalizer();
    obj = null;

    touchMinorGc();

    int x = 0;

  }

  @Override
  protected void finalize() throws Throwable {
    System.out.println(" finialize ");
  }

}

class文件信息如下

master:classes jerry$ javap -c com/hx/test06/Test03Finalizer.class 
Compiled from "Test03Finalizer.java"
public class com.hx.test06.Test03Finalizer {
  int f01;

  int f02;

  int f03;

  int f04;

  int f05;

  public com.hx.test06.Test03Finalizer();
    Code:
       0: aload_0
       1: invokespecial #1                  // Method java/lang/Object."<init>":()V
       4: aload_0
       5: ldc           #2                  // String identStr
       7: putfield      #3                  // Field identStr:Ljava/lang/String;
      10: return

  public static void main(java.lang.String[]);
    Code:
       0: new           #4                  // class com/hx/test06/Test03Finalizer
       3: dup
       4: invokespecial #5                  // Method "<init>":()V
       7: astore_1
       8: aconst_null
       9: astore_1
      10: invokestatic  #6                  // Method com/hx/test05/Test20DefNewGc.touchMinorGc:()V
      13: iconst_0
      14: istore_2
      15: return

  protected void finalize() throws java.lang.Throwable;
    Code:
       0: getstatic     #7                  // Field java/lang/System.out:Ljava/io/PrintStream;
       3: ldc           #8                  // String  finialize
       5: invokevirtual #9                  // Method java/io/PrintStream.println:(Ljava/lang/String;)V
       8: return
}

测试结果如下

Finalizer

类似于 Reference Handler 线程的初始化, Finalizer 的初始化是在 java 代码层面上进行初始化的

处理事情是, 不断从 Finalizer 的引用队列里面获取 FinalReference, 然后进行 runFinalizer 处理

runFinalizer 的处理也很简单, 如果该对象 没有执行过 finalize, 执行 finalize 方法

总结一下 Finalizer 做的事情就是 不断从 引用队列 里面获取 FinalReference 然后再 调用 referent 的 finalize 方法

引用队列 的数据是由 gc 处理之后, 放到 pendingList, 之后由 Reference Handler 转发到各个 引用 注册的 引用队列

为对象注册 Finalizer

注册的时机 取决于RegisterFinalizersAtInit 这个参数, 默认是 true, 表示在 构造函数 末尾注册 Finalizer

如果是 false 的话, 表示在 为对象分配了空间之后 注册

RegisterFinalizersAtInit 为 false

增加 vmOpt : -XX:-RegisterFinalizersAtInit

为对象分配了空间之后, 就向 Finalizer 注册了当前对象

定位一下当前的位置

warning: (x86_64) /Users/jerry/ClionProjects/HelloOpenJdk/jdk9/build/macosx-x86_64-normal-serverANDclient-slowdebug/jdk/bin/java empty dSYM file detected, dSYM was created with an executable with no debug info.
(lldb) re r
General Purpose Registers:
       rbx = 0x00000000000000bb
       rbp = 0x000070000b29f710
       rsp = 0x000070000b29f698
       r12 = 0x0000000000000000
       r13 = 0x0000000111ff7fe0
       r14 = 0x000070000b29f6a8
       r15 = 0x00007fcab7808000
       rip = 0x000000011b3f59f1
13 registers were unavailable.

(lldb) x 0x0000000111ff7fe0
0x111ff7fe0: bb 00 04 59 b7 02 00 4c 01 4c b8 03 00 03 3d b1  ...Y...L.L....=.
0x111ff7ff0: ff 00 34 41 12 1a 12 00 00 00 00 00 10 00 1c 00  ..4A............

在 main 方法里面 第一个 指令, new

注册的方式是 调用 Finalizer.register 注册

Finalizer.register 所做的事情, 就是创建了一个 Finalizer(继承自 FinalReference), 注册的队列为 一个全局的 queue

unfinalized 的链表用于关联所有的 注册了 Finalizer, 但是还没有 finalize 的 Finalizer

引用队列 的数据是由 gc 处理之后, 放到 pendingList, 之后由 Reference Handler 转发到各个 引用 注册的 引用队列, 然后 Finalizer 线程再来 消费 Finalizer.queue 队列, 执行 Finalizer.referent 的 finalize

RegisterFinalizersAtInit 为 true

去掉RegisterFinalizersAtInit 的手动设置, 默认为 true

这里的注册 就是在 构造方法 的末尾来进行注册了

定位一下当前的位置, 当前指令为 0xe8

warning: (x86_64) /Users/jerry/ClionProjects/HelloOpenJdk/jdk9/build/macosx-x86_64-normal-serverANDclient-slowdebug/jdk/bin/java empty dSYM file detected, dSYM was created with an executable with no debug info.
(lldb) re r
General Purpose Registers:
       rbx = 0x00000000000000e8
       rbp = 0x000070000c937710
       rsp = 0x000070000c937698
       r12 = 0x0000000000000000
       r13 = 0x000000011212c5a8
       r14 = 0x000070000c9375d0
       r15 = 0x00007fbb7b000800
       rip = 0x000000011650a9f1
13 registers were unavailable.

(lldb) x 0x000000011212c5a8
0x11212c5a8: e8 ff 00 64 00 00 00 00 00 00 00 00 01 00 18 00  ...d............
0x11212c5b8: 19 00 00 00 00 00 01 00 20 c6 12 12 01 00 00 00  ........ .......

但是你去查找规范, 你会发现根本没有 0xe8 对应的指令, 因为 这是一条 vm 自己使用的指令, 用于支持某些特性

到这里, 你可以先跳到07 运行时常量池索引的 rewrite的末尾的片尾彩蛋, 看一下

RegisterFinalizersAtInit 为 true 的时候, Object. 的最后一条Bytecodes::_return 更新成了Bytecodes::_return_register_finalizer

_return_register_finalizer 的业务处理

跳转之前 会调用InterpreterRuntime::register_finalizer, 把当前对象传递进去

也就是RegisterFinalizersAtInit 为 true 上面的截图是 执行_return_register_finalizer 的时候调用的InterpreterRuntime::register_finalizer

0xe8 指令为 _return_register_finalizer

finalize 骚操作之 起死回生

package com.hx.test06;

import static com.hx.test05.Test20DefNewGc.touchMinorGc;

/**
 * Test04FinalizerBackToAlive
 *
 * @author Jerry.X.He
 * @version 1.0
 * @date 2020-05-17 10:58
 */
public class Test04FinalizerBackToAlive {

  // identStr
  private String identStr = "identStr";
  private static Test04FinalizerBackToAlive obj = null;
  int f01;
  int f02;
  int f03;
  int f04;

  // Test04FinalizerBackToAlive
  // vmOpts : -Xint -server -Xmx600m -Xms600m -XX:PermSize=128M -XX:MaxPermSize=128M -XX:NewSize=128m -XX:MaxNewSize=128m -XX:SurvivorRatio=8 -XX:+UseSerialGC -XX:+PrintGCDetails
  public static void main(String[] args) {

    Test04FinalizerBackToAlive obj = new Test04FinalizerBackToAlive();
    obj = null;

    touchMinorGc();

    Test04FinalizerBackToAlive.obj = null;
    touchMinorGc();

  }

  @Override
  protected void finalize() throws Throwable {
    Test04FinalizerBackToAlive.obj = this;
    System.out.println(" finialize ");
  }

}

这里之所以 显式的 使用了Test04FinalizerBackToAlive.obj 是为了效果更加明显, 其实 第一次 minorgc 之后 obj 对应的对象是存活的

第二点就是 finalize 方法只执行了一次, 这一点可以参见上面runFinalizer 的代码, 可以自己调试一下 跟踪一下流程

finalize 骚操作之 致命异常

package com.hx.test06;

import static com.hx.test05.Test20DefNewGc.touchMinorGc;

/**
 * Test05FinalizerThrowEx
 *
 * @author Jerry.X.He
 * @version 1.0
 * @date 2020-05-17 10:58
 */
public class Test05FinalizerThrowEx {

  // identStr
  private String identStr = "identStr";
  int f01;
  int f02;
  int f03;
  int f04;
  int f05;

  // Test04FinalizerBackToAlive
  // vmOpts : -Xint -server -Xmx600m -Xms600m -XX:PermSize=128M -XX:MaxPermSize=128M -XX:NewSize=128m -XX:MaxNewSize=128m -XX:SurvivorRatio=8 -XX:+UseSerialGC -XX:+PrintGCDetails
  public static void main(String[] args) {

    Test05FinalizerThrowEx obj = new Test05FinalizerThrowEx();
    obj = null;

    touchMinorGc();

  }

  @Override
  protected void finalize() throws Throwable {
    if(f01 == 1) {
      throw new RuntimeException(" this is ex ");
//     finalize();
    }
    System.out.println(" finialize ");
  }

}

finalize 方法里面 尽量不要 做什么奇奇怪怪的事情, 就算你抛了异常, 结果什么什么反馈都看不到

finalize 骚操作之 阻塞

package com.hx.test06;

import static com.hx.test05.Test20DefNewGc.touchMinorGc;

/**
 * Test06FinalizerBlocked
 *
 * @author Jerry.X.He
 * @version 1.0
 * @date 2020-05-17 10:58
 */
public class Test06FinalizerBlocked {

  // identStr
  private String identStr = "identStr";
  int f01;
  int f02;
  int f03;
  int f04;
  int f05;

  // Test06FinalizerBlocked
  // vmOpts : -Xint -server -Xmx600m -Xms600m -XX:PermSize=128M -XX:MaxPermSize=128M -XX:NewSize=128m -XX:MaxNewSize=128m -XX:SurvivorRatio=8 -XX:+UseSerialGC -XX:+PrintGCDetails
  public static void main(String[] args) throws Exception {

    Test06FinalizerBlocked obj = new Test06FinalizerBlocked();
    obj = null;

    touchMinorGc();

    System.in.read();

  }

  @Override
  protected void finalize() throws Throwable {
    System.out.println(" finialize ");
    System.in.read();
  }

}

Finalizer 只是一个线程, 里面执行的是用户可以 自定义的方法, 如果 某对象的 finalize 阻塞, 或者 耗时过长, 则会对其他的 对象的 finalize 造成影响

参考

25 关于 Signal Dispatcher

26 关于 Attach Listener

27 关于 Reference Handler

07 运行时常量池索引的 rewrite

02 FinalReference.referent的回收时机

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值