gc-roots-reachability-analysis-savepoint 整理

savepoint, gc roots

  • 对象是否已死?

    • 引用计数
      • 解决不了循环引用问题
    • 可达性分析(从 gc roots搜索引用路径)
      • 虚拟机栈(栈帧中的本地变量表)中的-引用对象
      • 本地方法栈中-JNI(native方法)引用的对象
      • 方法区中-类静态属性引用的对象
      • 方法区中-常量引用的对象
    • 引用(上面可达性分析都涉及到了引用)
      • strong reference
      • soft reference
        • 将要发生内存溢出之前,将把这些对象列进回收范围之中进行第二次回收,如果这次回收还没有足够内存则抛出OOM
      • weak reference
        • 当gc工作时 无论当前内存是否够用 都会回收掉只有弱引用的对象
      • phantom reference
        • 为一个对象设置一个虚引用唯一目的是在这个对象被收集时受到一个系统通知
    • 生存还是死亡
      • 当可达性分析后,判断一个对象不可达或可达只是存在phantom,weak或soft在回收队列中时,则第一次标记并赛选
        • 赛选条件是:是否有必要执行finalize方法
          • 没有必要:对象没有覆盖finalize方法和finalize方法已经被JVM调用过了
      • 有必要执行finalize方法的放在f-queue队列当中,jvm自动创建且低优先级的Finalizer线程执行(这里是触发不保证等它运行结束)
      • finalize方法是对象逃离死亡的最后一次机会
      • 忘掉finalize这个方法的存在
    • 回收方法区
      • 废弃常量和无用的类
      • 无用的类(-Xnoclassgc,-verbose:class,-XX:+TraceClassLoading,-XX:+TraceClassUnloading)
        • 该类的所有实例都已经回收
        • 加载该类的classloader已经被回收
        • 该类对应的java.lang.Class对象没有被引用
  • gc roots

    • gc roots tracing
      • 时间不能耗太多
        • 不需要一个不漏的检查完所有的执行上下文和全局引用位置
        • OopMap的数据结构实现 直接从这里得知信息
      • stop the world
    • savepoint
      • 当GC需要中断线程的时候,不直接对线程操作,而是简单设置一个标志,各个线程执行时主动去轮询这个标志,标志为真时主动挂起
    • saveregion
      • 对应没有分到cpu的线程既: sleep, blocked等,无法响应中断
      • 安全区域指:一段代码片段中 引用关系不会发生变化 这个区域任何地方执行GC都是安全的
      • 在线程执行到了safe region中的代码时 标识自己进入了safe region 当在这段时间里JVM发起GC 就不用管标识自己为safe region状态的线程了
        • 在线程要离开safe region时 它要检查系统是否已经完成了根节点枚举或整个GC过程 如果完成就继续执行 否则等待收到可以离开safe region的信号位置
  • savepoint

    • 一直都知道,当发生GC时,正在执行Java code的线程必须全部停下来,才可以进行垃圾回收,这就是熟悉的STW(stop the world),但是STW的背后实现原理,比如这些线程如何暂停、又如何恢复?就比较疑惑了, 然而这一切的一切,都涉及到一个概念safepoint,openjdk的实现位于openjdk/hotspot/src/share/vm/runtime/safepoint.cpp
    • 什么是safepoint
      • safepoint可以用在不同地方,比如GC、Deoptimization,在Hotspot VM中,GC safepoint比较常见,需要一个数据结构记录每个线程的调用栈、寄存器等一些重要的数据
      • 从线程角度看,safepoint可以理解成是在代码执行过程中的一些特殊位置,当线程执行到这些位置的时候,说明虚拟机当前的状态是安全的,如果有需要,可以在这个位置暂停,
        • 比如发生GC时,需要暂停所以活动线程,但是线程在这个时刻,还没有执行到一个安全点,所以该线程应该继续执行,到达下一个安全点的时候暂停,等待GC结束。
    • 什么地方可以放safepoint
      • 理论上,在解释器的每条字节码的边界都可以放一个safepoint,不过挂在safepoint的调试符号信息要占用内存空间,如果每条机器码后面都加safepoint的话,需要保存大量的运行时数据,所以要尽量少放置safepoint,在safepoint会生成polling代码询问VM是否要“进入safepoint”,polling操作也是有开销的
      • 通过JIT编译的代码里,会在所有方法的返回之前,以及所有非counted loop的循环(无界循环)回跳之前放置一个safepoint,为了防止发生GC需要STW时,该线程一直不能暂停。另外,JIT编译器在生成机器码的同时会为每个safepoint生成一些“调试符号信息”,为GC生成的符号信息是OopMap,指出栈上和寄存器里哪里有GC管理的指针
  • 线程如何被挂起

    • 如果触发GC动作,VM thread会在VMThread::loop()方法中调用SafepointSynchronize::begin()方法,最终使所有的线程都进入到safepoint
      // Roll all threads forward to a safepoint and suspend them all void SafepointSynchronize::begin() { Thread* myThread = Thread::current(); assert(myThread->is_VM_thread(), "Only VM thread may execute a safepoint"); if (PrintSafepointStatistics || PrintSafepointStatisticsTimeout > 0) { _safepoint_begin_time = os::javaTimeNanos(); _ts_of_current_safepoint = tty->time_stamp().seconds(); } ... }
    • 在safepoint实现中,有这样一段注释,Java threads可以有多种不同的状态,所以挂起的机制也不同,一共列举了5中情况:
      • 1.执行java code
        • 在执行字节码时会检查safepoint状态,因为在begin方法中会调用Interpreter::notice_safepoints()方法,通知解释器更新dispatch table
      • 2.执行native code
        • 如果VM thread发现一个Java thread正在执行native code,并不会等待该Java thread阻塞,不过当该Java thread从native code返回时,必须检查safepoint状态,看是否需要进行阻塞
      • 3.执行compiled code
        • 如果想进入safepoint,则设置polling page不可读,当Java thread发现该内存页不可读时,最终会被阻塞挂起。在SafepointSynchronize::begin()方法中,通过 os::make_polling_page_unreadable()方法设置polling page为不可读
      • 4.线程处于Block状态
        • 即使线程已经满足了block condition,也要等到safepoint operation完成,如GC操作,才能返回
      • 5.线程正在转换状态
        • 会去检查safepoint状态,如果需要阻塞,就把自己挂起
      • 最终实现
        • 当线程访问到被保护的内存地址时,会触发一个SIGSEGV信号,进而触发JVM的signal handler来阻塞这个线程,The GC thread can protect some memory to which all threads in the process can write (using the mprotect system call) so they no longer can. Upon accessing this temporarily forbidden memory, a signal handler kicks in。再看看底层是如何处理这个SIGSEGV信号,实现位于hotspot/src/os_cpu/linux_x86/vm/os_linux_x86.cpp 执行os::block_on_serialize_page_trap()把当前线程阻塞挂起
    • 线程如何恢复
      • 有了begin方法,自然有对应的end方法,在SafepointSynchronize::end()中,会最终唤醒所有挂起等待的线程,大概实现如下:
      • 1.重新设置pooling page为可读
      • 2.设置解释器为ignore_safepoints
      • 3.唤醒所有挂起等待的线程
  • 对JVM性能有什么影响

    • 通过设置JVM参数 -XX:+PrintGCApplicationStoppedTime, 可以打出系统停止的时间
    • 一个大概率的原因是当发生GC时,有线程迟迟进入不到safepoint进行阻塞,导致其他已经停止的线程也一直等待,VM Thread也在等待所有的Java线程挂起才能开始GC,这里需要分析业务代码中是否存在有界的大循环逻辑,可能在JIT优化时,这些循环操作没有插入safepoint检查
  • References

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 199,711评论 5 468
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 83,932评论 2 376
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 146,770评论 0 330
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 53,799评论 1 271
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 62,697评论 5 359
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,069评论 1 276
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,535评论 3 390
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,200评论 0 254
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,353评论 1 294
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,290评论 2 317
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,331评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,020评论 3 315
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,610评论 3 303
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,694评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,927评论 1 255
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,330评论 2 346
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 41,904评论 2 341

推荐阅读更多精彩内容

  • 这篇文章是我之前翻阅了不少的书籍以及从网络上收集的一些资料的整理,因此不免有一些不准确的地方,同时不同JDK版本的...
    高广超阅读 15,511评论 3 83
  • http://www.cnblogs.com/angeldevil/p/3801189.html值得一看 Clas...
    snail_knight阅读 1,403评论 1 0
  • 1.什么是垃圾回收? 垃圾回收(Garbage Collection)是Java虚拟机(JVM)垃圾回收器提供...
    简欲明心阅读 89,313评论 17 311
  • Java SE 基础: 封装、继承、多态 封装: 概念:就是把对象的属性和操作(或服务)结合为一个独立的整体,并尽...
    Jayden_Cao阅读 2,083评论 0 8
  • 在这个世界上,应该有很多人,都躲在云后面,悄悄看看自己喜欢的人吧?
    没理想的猪阅读 532评论 0 49