你必须了解的java内存管理机制(三)

  • 时间:
  • 浏览:4
  • 来源:九妹赚钱网 - 专注共享墨天逸博客技术

相关链接(注:文章讲解JVM以Hotspot虚拟机为例,jdk版本为1.8)

1、 你还要了解的java内存管理机制-运行时数据区

2、 你还要了解的java内存管理机制-内存分配

3、 你还要了解的java内存管理机制-垃圾标记

  2、主动式中断:不强制中断系统进程池,只是简单地设置另十个 中断标记,各个系统进程池在执行时主动轮询累似 标记,一旦发现标记被改变(总是出先中断标记)时,就将当事人中断挂起。目前所有商用虚拟机删剪采用主动式中断。

  优点:实现简单,传输传输速率高。

  缺点:真难避免对象之间的相互循环引用。且开销较大,频繁的引用变化会带来多量的额外运算。在谈实现思路的之前 有只是一句话“任好久刻计数器值为0,则认为对象是不再被使用的”。之前 通过里边的例子朋友 还要看到,着实 对象因为着不再使用了,但计数器的值仍然是1,之前 之前 这另十个 对象不需要被标记为垃圾。

  现状:主流的JVM都真难取舍 引用计数法来管理内存。

  为避免上述难题,HotSpot 采用了累似 “准确式GC” 的技术,该技术主要功能只是让虚拟机还要准确的知道内存中某个位置的数据类型是累似 ,比如某个内存位置到底是另十个 整型的变量,还是对某个对象的reference,只是在进行 GC Roots枚举时,只还要枚举reference类型的即可。那之前 虚拟机准确的知道累似 位置发生的是reference类型数据呢?OopMap+RememberedSet!

  在可达性分析后发现到GC Roots真难任何引用链相连时,被第一次标记。之前 判断此对象与否必要执行finalize()法律法律依据!因为着对象真难覆盖finalize()法律法律依据因为着finalize()因为着被JVM调用过,则累似 对象就会认为是垃圾,还要回收。对于覆盖了finalize()法律法律依据,且finalize()法律法律依据真难被JVM调用过时,对象会被倒进另十个 成为F-Queue的队列中,等待歌曲着被触发调用对象的finalize()法律法律依据。

  1、抢先式中断:不还要系统进程池的执行代码去主动配合,当发生GC时,先强制中断所有系统进程池,之前 因为着发现之前 系统进程池未发生安全点,真难将其唤醒,直至其到达安全点再次将其中断。只是总是等待歌曲所有系统进程池全是安全点后始于英语 GC。

  finalize()法律法律依据是被第一次标记对象的逃脱死亡的最后一次因为着。在jvm中,另十个 对象的finalize()法律法律依据只会被系统调用一次,经过finalize()法律法律依据逃脱死亡的对象,第二次不需要再调用。因为着该法律法律依据是在对象进行回收的之前 调用,之前 之前 还要在该法律法律依据中实现资源关闭的操作。之前 ,因为着该法律法律依据执行的时间是不取舍 的,甚至,在java系统进程池池不正常退出的情况汇报下该法律法律依据全是全是执行!之前 之前 在正常情况汇报下,尽量避免使用!因为着还要"释放资源",还要定义显式的终止法律法律依据,并在"try-catch-finally"的finally{}块中保证及时调用,如File相关类的close()法律法律依据。下面朋友 看另十个 在finalize中逃脱死亡的栗子吧:

  对应的引用关系图应该如下(注意引用计数器值的变化):

  通过上篇文章的知识朋友 知道,当法律法律依据执行的之前 ,法律法律依据的局部变量表和堆的关系应该是如下图的(注意堆中对象头中红色括号内的数字,只是引用计数器,这里只是举栗,实际实现因为着会有差异):

  伟大的发明来的p1和p2另两当事人,你要 你要 们互为最好的朋友 ,于是代码如下:

  实现思路:给对象加进另十个 引用计数器,每当另十个 地方引用它,计数器加1。当引用失效,计数器值减1。任好久刻计数器值为0,则认为对象是不再被使用的。举个小栗子,朋友 另十个 People的类,People类有id和bestFriend的属性。朋友 用People类来造另十个 小人:

  这之前 引用关系图就变成如下了,因为着p1和p2对象还相互引用着,之前 之前 引用计数器的值还为1。

  执行结果如下,具体就太大说啦,不明白的就当事人动手去试试吧!

  

  OopMap避免了枚举根节点耗时的难题,之前 分代分派的难题依然发生!这之前 就还要另一利器了- RememberedSet。对于发生不同年代对象之间的引用关系,会在引用关系发生时,在新生代边上专门开辟一块空间记录下来,这只是RememberedSet!之前 之前 “新生代的 GC Roots ” + “ RememberedSet存储的内容”,才是新生代分派时真正的GC Roots(G1 分派器也使用了 RememberedSet 累似 技术)。

  

  

  实现思路:通过GC Roots的对象作为起始点,从累似 节点向下搜索,搜索走过的路径成为引用链,当另十个 对象到GC Root真难任何引用链相连时,则证明对象是不可用的。如下图,红色的有几个对象因为着真难跟GC Root真难任何引用链相连,之前 之前 会进行标记。

  优点:还要很好的避免对象相互循环引用的难题。

  缺点:实现比较错综复杂;还要分析多量数据,消耗多量时间;

  现状:主流的JVM(如HotSpot)都取舍 可达性分析来管理内存。

  因为着是进行根节点枚举,朋友 真难全栈扫描,找到变量表中存放为reference类型的变量,之前 找到堆中对应的对象,最后遍历对象的数据(如属性等),找到对象数据中存放为指向之前 reference的对象……只是的开销无疑是非常大的!

  HotSpot在OopMap的帮助下还要快速且准确的完成GC Roots枚举,之前 在运行过程中,非常多的指令全是因为着引用关系变化,因为着为累似 指令都生成对应的OopMap,还要的空间成本太高。之前 之前 只在特定的位置记录OopMap引用关系,累似 位置称为安全点(Safepoint)。怎么在GC发生时让所有系统进程池(不包括JNI系统进程池)运行到其所在最近的安全点上再停顿下来?这里有累似 方案:

  通过里边可达性分析朋友 了解了有累似 GC Root,了解了通过累似 GC Root去搜寻并标记对象是生存还是死亡的思路。之前 具体的实现只是那张图显示的真难简单吗?当然全是,因为着朋友 的堆是分代分派的,那GC Root连接的对象因为着在新生代,也因为着在老年代,新生代的对象因为着会引用老年代的对象,老年代的对象也因为着引用新生代。因为着直接通过GC Root去搜寻,则每次全是遍历整个堆,那分代分派就真难实现了呢!之前 ,枚举整个根节点的之前 是还要系统进程池停顿的(保证一致性,还能够 总是出先正在枚举 GC Roots,而系统进程池池还在跑的情况汇报,这会因为着 GC Roots 不断变化,产生数据不一致因为着统计不准确的情况汇报),而枚举根节点又比较耗时,这在大并发高访问量情况汇报下,分分钟就会因为着系统瘫痪!啥意思呢,下面一张图感受一下:

  

  通过上篇文章朋友 知道,JVM创建对象全是通过累似 法律法律依据从内存中划分一块区域进行分配。真难当朋友 服务器源源不断的接收请求的之前 ,就会频繁的还要进行内存分配的操作,之前 朋友 服务器的内存确是非常有限的呢!之前 之前 对不再使用的内存进行回收再利用就成了JVM肩负的重任了! 真难,摆在JVM身前的难题来了,为什么在么在判断累似 内存不再使用了?为什么在么在合理、高效的进行回收操作?既然要回收,那第一步只是要找到还要回收的对象!

  使用安全点似乎因为着完美避免怎么进入GC的难题了,之前 GC发生的之前 ,某个系统进程池正在睡觉(sleep),无法响应JVM的中断请求,这之前 系统进程池一旦醒来就会继续执行了,这会因为着引用关系发生变化呢!之前 之前 还要安全区域的思路来避免累似 难题。系统进程池执行进入安全区域,首先标识当事人因为着进入安全区域。系统进程池被唤醒背叛安全区域时,其还要检查系统算因为着完成根节点枚举(或整个GC)。因为着因为着完成,就继续执行,之前 还要等待歌曲,直到收到还要安全背叛Safe Region的信号通知!

  之前 朋友 再做之前 避免,去除变量和堆中对象的引用关系。

  通过可达性分析还要对还要回收的对象进行标记,与否标记的对象全是被回收呢?并全是呢!要真正签署另十个 对象的死亡,大概要经历两次的标记过程!

  前面花了两篇文章对JVM的内存管理机制做了较多的介绍,通过第一篇文章先了解了JVM的运行时数据区,之前 在第二篇文章中通过另十个 创建对象的实例介绍了JVM的内存分配的相关内容!真难,万众瞩目的JVM垃圾回收是之前 登场了!JVM垃圾回收这块的内容相对较多、较错综复杂。之前 ,你要做好JVM的性能调优,这块的内容又还要了解和掌握!

本文在当事人技术博客不同步发布,详情可用力戳

亦可扫描屏幕右侧二维码关注当事人公众号,公众号内有当事人联系法律法律依据,等你来撩...

  

  安全点既还能够 太大,以至于 GC 过程等待歌曲系统进程池池到达安全点的时间过长,只是能太大,以至于 GC 过程带来的成本过高 。安全点的选定基本上是以系统进程池池“与否具有让系统进程池池长时间执行的形状”为标准进行选定的,累似 法律法律依据调用、循环跳转、异常跳转等,之前 之前 具有累似 功能的指令才会产生安全点(在主动式中断中,轮询标志的地方和安全点是重合的,之前 之前 系统进程池在遇到累似 指令时全是去轮询中断标志!)。

  OopMap记录了栈上本地变量到堆上对象的引用关系,在GC发生时,系统进程池会运行到最近的另十个 安全点停下来,之前 更新当事人的OopMap,记下栈上累似 位置代表着引用。枚举根节点时,递归遍历每个栈帧的OopMap,通过栈中记录的被引用对象的内存地址,即可找到累似 对象( GC Roots )。只是,OopMap就避免了全栈扫描,加快枚举根节点的传输传输速率。

  

  执行完第一次的标记后,GC将对F-Queue队列中的对象进行第二次小规模标记。也只是执行对象的finalize()法律法律依据!因为着对象在其finalize()法律法律依据中重新与引用链上任何另十个 对象建立关联,第二次标记全是将其移出"即将回收"的集合。因为着对象真难,也还要认为对象已死,还要回收了。