目的和算法-GC-渣滓回收-渣滓回收及其原理-JVM (目的计算方法)
图片
1渣滓回收机制(GC是什么?为什么要GC)
为了让程序员更专一于代码的成功,而不用过多的思索内存监禁的疑问,所以,在Java言语中,有了智能的渣滓回收机制,也就是咱们相熟的GC(GarbageCollection)。
有了渣滓回收机制后,程序员只要要关心内存的放开即可,内存的监禁由系统智能识别成功。
在启动渣滓回收时,不同的对象援用类型,GC会驳回不同的回收机遇
换句话说,智能的渣滓回收的算法就会变得十分关键了,假设由于算法的不正当,造成内存资源不时没有监禁,雷同也或许会造成内存溢出的。
当然,除了Java言语,C#、/target=_blankclass=infotextkey>Python等言语也都有智能的渣滓回收机制。
2对象什么时刻可以被渣滓器回收
图片
繁难一句就是:假设一个或多个对象没有任何的援用指向它了,那么这个对象如今就是渣滓,假设定位了渣滓,则有或许会被渣滓回收器回收。
假设要定位什么是渣滓,有两种形式来确定,第一个是援用计数法,第二个是可达性剖析算法
2.1援用计数法
一个对象被援用了一次性,在以后的对象头上递增一次性援用次数,假设这个对象的援用次数为0,代表这个对象可回收
Stringdemo=newString("123");
Stringdemo=null;
图片
当对象间出现了循环援用的话,则援用计数法就会失效
图片
先口头右侧代码的前4行代码
图片
目前上方的援用相关和计数都是没疑问的,然而,假设代码继续往下口头,如下图
图片
虽然a和b都为null,然而由于a和b存在循环援用,这样a和b永远都不会被回收。
优势:
缺陷:
2.2可达性剖析算法
如今的虚构机驳回的都是经过可达性剖析算法来确定哪些内容是渣滓。
会存在一个根节点【GCRoots】,引出它上方指向的下一个节点,再以下一个节点节点开局找出它上方的节点,依次往下类推。直到一切的节点所有遍历终了。
图片
X,Y这两个节点是可回收的,然而并不会马上的被回收!!对象中存在一个方法【finalize】。当对象被标志为可回收后,当出现GC时,首先会判别这个对象能否口头了finalize方法,假设这个方法还没有被口头的话,那么就会先来口头这个方法,接着在这个方法口头中,可以设置以后这个对象与GCROOTS发生关联,那么这个方法口头成功之后,GC会再次判别对象能否可达,假设依然无法达,则会启动回收,假设可达了,则不会启动回收。
finalize方法关于每一个对象来说,只会口头一次性。假设第一次性口头这个方法的时刻,设置了以后对象与RCROOTS关联,那么这一次性不会启动回收。那么等到这个对象第二次被标志为可回收时,那么该对象的finalize方法就不会再次口头了。
/***demo是栈帧中的本地变量,当demo=null时,由于此时demo充任了GCRoot的作用,demo与原来指向的实例newDemo()断开了衔接,对象被回收。*/publicclassDemo{publicstaticvoidmn(String[]args){Demodemo=newDemo();demo=null;}}
/***当栈帧中的本地变量b=null时,由于b原来指向的对象与GCRoot(变量b)断开了衔接,所以b原来指向的对象会被回收,而由于咱们给a赋值了变量的援用,a在此时是类静态属性援用,充任了GCRoot的作用,它指向的对象依然存活!*/publicclassDemo{publicstaticDemoa;publicstaticvoidmain(String[]args){Demob=newDemo();b.a=newDemo();b=null;}}
/***常量a指向的对象并不会由于demo指向的对象被回收而回收*/publicclassDemo{publicstaticfinalDemoa=newDemo();publicstaticvoidmain(String[]args){Demodemo=newDemo();demo=null;}}
3JVM渣滓回收算法有哪些?
3.1标志肃清算法
标志肃清算法:是将渣滓回收分为2个阶段,区分是标志和肃清。
1.依据可达性剖析算法得出的渣滓启动标志
2.对这些标志为可回收的内容启动渣滓回收
图片
可以看到,标志肃清算法处置了援用计数算法中的循环援用的疑问,没有从root节点援用的对象都会被回收。
雷同,标志肃清算法也是有缺陷的:
3.2复制算法
假设您感觉本文不错,欢迎关注,点赞,收藏允许,您的关注是我坚持的能源!
复制算法的外围,将原有的内存空间一分为二,每次只用其中的一块,在渣滓回收时,将正在经常使用的对象复制到另一个内存空间中,而后将该内存空间清空,替换两个内存的角色,成功渣滓的回收。
假设内存中的渣滓对象较多,须要复制的对象就较少,这种状况下适宜经常使用该形式并且效率比拟高,反之,则不适宜。
1)将内存区域分红两部分,每次操作其中一个。
2)当启动渣滓回收时,将正在经常使用的内存区域中的存活对象移动到未经常使用的内存区域。当移动完对这部分外存区域一次性性肃清。
3)循环往返。
优势:
缺陷:
3.3标志整顿算法
标志紧缩算法:是在标志肃清算法的基础之上,做了优化改良的算法。和标志肃清算法一样,也是从根节点开局,对对象的援用启动标志,在清算阶段,并不是繁难的直接清算可回收对象,而是将存活对象都向内存另一端移动,而后清算边界以外的渣滓,从而处置了碎片化的疑问。
图片
1)标志渣滓。
2)须要肃清向左边走,不须要肃清的向左边走。
3)肃清边界以外的渣滓。
优缺陷同标志肃清算法,处置了标志肃清算法的碎片化的疑问,同时,标志紧缩算法多了一步,对象移动内存位置的步骤,其效率也有有必定的影响。
与复制算法对比:复制算法标志完就复制,但标志整顿算法得等把一切存活对象都标志终了,再启动整顿
4分代搜集算法
4.1概述
在java8时,堆被分为了两份:重生代和老年代【1:2】,在java7时,还存在一个终身代。
关于重生代,外部又被分为了三个区域。Eden区,S0区,S1区【8:1:1】
当对重生代发生GC:MinorGC【youngGC】
当对老年代代发生GC:MajorGC
当对重生代和老年代发生FullGC:重生代+老年代完整渣滓回收,暂停期间长,应尽力防止
4.2上班机制
MinorGC、MixedGC、FullGC的区别是什么
图片
名词解释:
STW(Stop-The-World):暂停一切运行程序线程,期待渣滓回收的成功
5说一下JVM有哪些渣滓回收器?
在jvm中,成功了多种渣滓搜集器,包含:
5.1串行渣滓搜集器
Serial和SerialOld串行渣滓搜集器,是支经常使用复线程启动渣滓回收,堆内存较小,适宜团体电脑
渣滓回收时,只要一个线程在上班,并且java运行中的一切线程都要暂停(STW),期待渣滓回收的成功。
图片
5.2并行渣滓搜集器
ParallelNew和ParallelOld是一个并行渣滓回收器,JDK8自动经常使用此渣滓回收器
渣滓回收时,多个线程在上班,并且java运行中的一切线程都要暂停(STW),期待渣滓回收的成功。
图片
5.3CMS(并发)渣滓搜集器
CMS全称ConcurrentMarkSweep,是一款并发的、经常使用标志-肃清算法的渣滓回收器,该回收器是针对老年代渣滓回收的,是一款以失掉最短回收停登期间为指标的搜集器,停登期间短,用户体验就好。其最大特点是在启动渣滓回收时,运行依然能反常运转。
图片
6详细聊一下G1渣滓回收器
6.1概述
图片
image-20230506154323950
6.2YoungCollection(年轻代渣滓回收)
图片
图片
图片
图片
图片
图片
图片
6.3YoungCollection+ConcurrentMark(年轻代渣滓回收+并发标志)
当老年代占用内存超越阈值(自动是45%)后,触发并发标志,这时无需暂停用户线程
图片
图片
6.4MixedCollection(混合渣滓回收)
复制成功,内存失掉监禁。进入下一轮的重生代回收、并发标志、混合搜集
图片
其中H叫做巨型对象,假设对象十分大,会开拓一块延续的空间存储巨型对象
图片
7.JVM渣滓回收面试
面试官:简述Java渣滓回收机制?(GC是什么?为什么要GC)
候选人:
面试官:对象什么时刻可以被渣滓器回收
候选人:
面试官:JVM渣滓回收算法有哪些?
候选人:
面试官:你能详细聊一下分代回收吗?
候选人:
面试官:讲一下重生代、老年代、终身代的区别?
候选人:
面试官:说一下JVM有哪些渣滓回收器?
候选人:
面试官:MinorGC、MajorGC、FullGC是什么
候选人:
JVM废品收集机制
JVM废品回收机制是java程序员必须要了解的知识,对于程序调优具有很大的帮助(同时也是大厂面试必问题)。
要了解废品回收机制,主要从三个方面:
(1)废品回收面向的对象是谁?
(2)废品回收算法有哪些?
(3)废品收集器有哪些?每个收集器有什么特点。
接下来一一讲解清楚:
一、废品回收面向的对象
也就是字面意思, 废品 回收嘛,重要的是废品,那什么对象是废品呢,简单来说就是无用的或已死的对象。这样又引申出来什么对象是已死的,怎么判断对象是否已死?
判断对象是否已死有两种算法和对象引用分类:
(1)引用计数算法:
也是字面意思,通过给对象添加引用计数器,添加引用+1,反之-1。当引用为0的时候,此时对象就可判断为无用的。
优点:实现简单,效率高。
缺点:无法解决循环引用(就是A引用B,B也引用A的情况)的问题。
(2)根搜索算法(也就是GC Roots):
通过一系列称为GC Roots的对象,向下搜索,路径为引用链,当某个对象无法向上搜索到GC Roots,也就是成为GC Roots不可达,则为无用对象。
如果一个对象是GC Roots不可达,则需要经过两次标记才会进行回收,第一次标记的时候,会判断是否需要执行finalize方法(没必要执行的情况:没实现finalize方法或者已经执行过)。如果需要执行finalize方法,则会放入一个回收队列中,对于回收队列中的对象,如果执行finalize方法之后,没法将对象重新跟GC Roots进行关联,则会进行回收。
很抽象,对吧,来一个明了的解释?
比如手机坏了(不可达对象),有钱不在乎就直接拿去回收(这就是没实现finalize方法),如果已经修过但是修不好了(已经执行过finalize方法),就直接拿去回收站回收掉。如果没修过,就会拿去维修店(回收队列)进行维修,实在维修不好了(执行了finalize方法,但是无法连上GC Roots),就会拿去回收站回收掉了。
那什么对象可以成为GC Roots呢?
1>虚拟机栈中的引用对象
2>本地方法栈中Native方法引用的对象
2>方法区静态属性引用对象
3>方法区常量引用对象
(3)对象引用分类
1>强引用:例如实例一个对象,就是即使内存不够用了,打死都不回收的那种。
2>软引用:有用非必须对象,内存够,则不进行回收,内存不够,则回收。例如A借钱给B,当A还有钱的时候,B可以先不还,A没钱了,B就必须还了。
3>弱引用:非必须对象,只能存活到下一次废品回收前。
4>虚引用:幽灵引用,必须跟引用队列配合使用,目的是回收前收到系统通知。
下面是java的引用类型结构图:
(1)软引用示例
内存够用的情况:
运行结果:
![JVM](https://tyye.cn/zb_users/theme/tpure/style/images/lazyload.png)
内存不够用的情况:
运行结果:
(2)弱引用示例结果:
无论如何都会被回收
(3)虚引用示例:
运行结果:
解释:为什么2和5的输出为null呢,如下
3为null是因为还没有进行gc,所以对象还没加入到引用队列中,在gc后就加入到了引用队列中,所以6有值。
这个虚引用在GC后会将对象放到引用队列中,所以可以在对象回收后做相应的操作,判断对象是否在引用队列中,可以进行后置通知,类似spring aop的后置通知。
二、废品回收发生的区域
废品回收主要发生在堆内存里面,而堆内存又细分为 年轻代 和 老年代 ,默认情况下年轻代和老年代比例为1:2,比如整个堆内存大小为3G,年轻代和老年代分别就是1G和2G,想要更改这个比例需要修改JVM参数-XX:NewRatio,
比如-XX:NewRatio=4,那老年代:年轻代=4:1。而年轻代又分为Eden区,S0(Survivor From)和S1(Survivor To)区,一般Eden:S0:S1=8:1:1,如果想要更改此比例,则修改JVM参数-XX:SurvivorRatio=4,此时就是Eden:S0:S1=4:1:1。
在年轻代发生GC称为Young GC,老年代发生GC成为Full GC,Young GC比Full GC频繁。
解析Young GC:
JVM启动后,第一次GC,就会把Eden区存活的对象移入S0区;第二次GC就是Eden区和S0一起GC,此时会把存活的对象移入S1区,S0清空;第三次GC就是Eden区和S1区进行GC,会把存活的对象移入S0区,如此往复循环15次(默认),就会把存活的对象存入老年区。
类似与如果有三个桶,编号分别为1(1号桶内的沙子是源源不断的,就像工地上。你们没去工地搬过砖可能不知道,但是我真的去工地上搬过啊),2,3。1里面装有沙子,需要将沙子筛为细沙。首先将桶1内的沙子筛选一遍过后的放置于桶2,第二次筛选就会将桶1和桶2里面的沙子一起筛,筛完之后放到桶3内,桶2清空。第三次筛选就会将桶1和桶3的沙子一起筛选,晒完放到桶2内,桶3清空。如此往复循环15次,桶2或桶3里面的沙子就是合格的沙子,就需要放到备用桶内以待使用。
上述中桶1就是Eden区,桶2就是S0区,桶3就是S1区。
三、废品回收算法
三种,分别是复制算法,标记-清除算法,标记-整理算法。
(1)复制算法。
其会将内存区域分成同样大小的两块,一块用来使用,另外一块在GC的时候存放存活的对象,然后将使用的一块清除。如此循环往复。
适用于新生代。
优点:没有内存碎片,缺点:只能使用一般的内存。
(2)标记-清除算法。
使用所有内存区域,在GC的时候会将需要回收的内存区域先进行标记,然后同意回收。
适用于老年代。
缺点:产生大量内存碎片,会直接导致大对象无法分配内存。
(3)标记-整理算法。
使用所有内存区域,在GC的时候会先将需要回收的内存区域进行标记,然后将存活对象忘一边移动,最后将清理掉边界以外的所有内存。
适用于老年代。
四、GC日志查看
利用JVM参数-XX:+PrintGCDetails就可以在GC的时候打印出GC日志。
年轻代GC日志:
老年代GC日志:
五、废品收集器
主要有四类收集器以及七大收集器
四类:
(1)Serial:单线程收集器,阻塞工作线程,它一工作,全部都得停下。
(2)Paralle:Serial的多线程版本,也是阻塞工作线程
(3)CMS(ConcMarkSweep):并行废品收集器,可以和工作线程一起工作。
(4)G1:将堆分成大小一致的区域,然后并发的对其进行废品回收。
怎么查看默认的收集器呢?
用JVM参数-XX:+PrintCommandLineFlags,运行之后会输出如下参数。可以看到,jdk1.8默认是Parallel收集器。
七大收集器:
(1)Serial:串行废品收集器,单线程收集器。用于新生代。用JVM参数-XX:+UseSerialGC开启,开启后Young区用Serial(底层复制算法),Old区用Serial Old(Serial的老年代版本,底层是标记整理算法)。
(2)ParNew:用于新生代,并行收集器。就是Serial的多线程版本。用JVM参数-XX:+UseParNewGC,young:parnew,复制算法。Old:serialOld,标记整理算法。-XX:ParallecGCThreads限制线程回收数量,默认跟cpu数目一样。只是新生代用并行,老年代用串行。
(3)Parallel Scavenge:并行回收收集器。用JVM参数-XX:+UseParallelGC开启,young:parallel scavenge(底层是复制算法),old:parallel old(parallel的老年代版本,底层是标记整理),新生代老年代都用并行回收器。
这个收集器有两个优点:
可控的吞吐量 :就是工作线程工作90%的时间,回收线程工作10%的时间,即是说有90%的吞吐量。
自适应调节策略 :会动态调节参数以获取最短的停顿时间。
(4)Parallel Old:Parallel Scavenge的老年代版本,用的是标记整理算法。用JVM参数-XX:+UseParallelOldGC开启,新生代用Parallel Scavenge,老年代用Parallel Old
(5)CMS(ConcMarkSweep):并发标记清除。 底层是标记清除算法,所以会产生内存碎片,同时也会耗cpu。 以获取最短回收停顿时间为目标。-XX:+UseConcMarkSweep,新生代用ParNew,老年代用CMS。CMS必须在堆内存用完之前进行清除,否则会失败,这时会调用SerialOld后备收集器。
初始标记和重新标记都会停止工作线程,并发标记和并发清除会跟工作线程一起工作。
(6)SerialOld:老年代串行收集器(以后Hotspot虚拟机会直接移除掉)。
(7)G1:G1废品收集器,算法是标记整理,不会产生内存碎片。横跨新生代老年代。实现尽量高吞吐量,满足回收停顿时间更短。
G1可以精确控制废品收集的停顿时间,用JVM参数-XX:MaxGCPauseMillis=n,n为停顿时间,单位为毫秒。
区域化内存划片Region,会把整个堆划分成同样大小的区域块(1MB~32MB),最多2048个内存区域块,所以能支持的最大内存为32*2048=MB,约为64G。
上图是收集前和收集后的对比,有些对象很大,分割之后就是连续的区域,也即是上图的Humongous。
上述理论可能有点乏味,下图很清晰明了(某度找的)。
下面来一张整个废品回收机制的思维导图(太大,分成两部分)。
我是Liusy,一个喜欢健身的程序猿。
欢迎关注【Liusy01】,一起交流Java技术及健身,获取更多干货。
如何通知java虚拟机进行废品回收?以及废品回收机制的原理是什么
java的废品回收会由虚拟机自动进行。 因为各版本虚拟机的实现不一样,具体回收时点会有一定的不同,但大体上在对内存不足时,是一定会尝试进行一次回收的。 如果回收后,内存还是不够,则会报出经典的OutofMemory异常。 用户可以调用()进行强制的内存回收,但和上面一样,回收完后不一定就保证能有足够的内存。 具体原理你可以想象为虚拟机会保存一张森林结构的内存对象表,林中各树的根节点是各个线程,线程中引用的对象,以及这些对象引用的其他对象会按照引用关系依次排列分布到树中。 这样当GC进行时,依次扫描所有对象,如果一个对象的父引用指向不到一个处于活动状态的线程,或者所有直接父引用已经标记为可回收,则将这个对象标记为可回收。 最后再释放所有标记为可回收的对象内存,达到清理内存废品的目的。
免责声明:本文转载或采集自网络,版权归原作者所有。本网站刊发此文旨在传递更多信息,并不代表本网赞同其观点和对其真实性负责。如涉及版权、内容等问题,请联系本网,我们将在第一时间删除。同时,本网站不对所刊发内容的准确性、真实性、完整性、及时性、原创性等进行保证,请读者仅作参考,并请自行核实相关内容。对于因使用或依赖本文内容所产生的任何直接或间接损失,本网站不承担任何责任。