吾爱破解 - 52pojie.cn

 找回密码
 注册[Register]

QQ登录

只需一步,快速开始

查看: 1430|回复: 0
收起左侧

[Java 转载] 垃圾回收

[复制链接]
YVKG 发表于 2021-11-24 18:32
先来讲一下垃圾回收的前置知识。

1.Java内存划分

2.Java堆

3.Minor GC与Full GC

4.Java引用的四种状态

5.Java对象在内存中的3种状态

1.Java内存划分


上面这张图就是jvm运行时的状态。具体划分为如下5个内存空间:

1-3为线程私有,4-5为线程共享

1、程序计数器:为了线程切换后能恢复到正确的执行位置。线程私有
2、Java虚拟机栈:虚拟机栈描述的是Java方法执行的内存模型:方法被调用时创建栈帧-->局部变量表->局部变量、对象引用。线程私有
3、本地方法栈:为虚拟机执使用到的Native方法服务。线程私有
4、Java堆:存放所有new出来的东西,在虚拟机启动时创建,垃圾收集器管理的主要区域。线程共享
5、方法区:存储被虚拟机加载的类信息、常量、静态常量、静态方法等。线程共享
   5、1运行时常量池(方法区的一部分)
2.Java堆

Java堆是垃圾收集器管理的主要区域,因此很多时候也被称谓“GC堆”。从内存回收的角度来看,由于现在收集器基本都采用分代收集算法,所以Java堆中还可以细分为:新生代和老年代:在细致一点的有Eden空间、From Survivor空间、To Survivor空间等。

3.Minor GC 与 Full GC
Minor GC:
简述:Minor GC是发生在【新生代】中的垃圾收集动作,采用的是【复制算法】,因为Java对象大多都具备朝生夕灭的特性,所以Minor GC非常频繁,一般回收速度也比较快。

详述:对象在Eden和From区出生后,在经过一次Minor GC后,如果对象还存活,并且能够被to区所容纳,那么在使用【复制算法】时这些存活对象就会被复制到to区域,然后清理掉Eden区和from区,并将这些对象的年龄设置为1,以后对象在Survivor区每熬过一次Minor GC,就将对象的年龄+1,当对象的年龄达到某个值时(默认是15岁,可以通过参数 --XX:MaxTenuringThreshold设置),这些对象就会成为老年代。

但这也是不一定的,对于一些较大的对象(即需要分配一块较大的连续内存空间)则是直接进入老年代

Full GC:
简述:Full GC是发生在【老年代】的垃圾收集动作,采用的是【标记-清除/整理算法】,速度慢,一般会比Minor GC的速度慢10倍以上。

详述:【老年代】里的对象几乎都是在Survivor区熬过来的,不会那么容易死掉。因此Full GC发生的次数不会有Minor GC那么频繁,并且做一次Full GC要比做一次Minor GC的时间要长。

另外,如果采用的是【标记-清除算法】的话会产生许多碎片,此后如果需要为较大的对象分配内存空间时,若无法找到足够的连续的内存空间,就会提前触发一次GC。

4.Java引用的四种状态
强引用:

  用的最广。我们平时写代码时,new一个Object存放在堆内存,然后用一个引用指向它,这就是强引用。

  如果一个对象具有强引用,那垃圾回收器绝不会回收它。当内存空间不足,Java虚拟机宁愿抛出OutOfMemoryError错误,使程序异常终止,也不会靠随意回收具有强引用的对象来解决内存不足的问题。

软引用:

  如果一个对象只具有软引用,则内存空间足够时,垃圾回收器就不会回收它;如果内存空间不足了,就会回收这些对象的内存。(备注:如果内存不足,随时有可能被回收。)

  只要垃圾回收器没有回收它,该对象就可以被程序使用。软引用可用来实现内存敏感的高速缓存。

弱引用:

  弱引用与软引用的区别在于:只具有弱引用的对象拥有更短暂的生命周期。

  每次执行GC的时候,一旦发现了只具有弱引用的对象,不管当前内存空间足够与否,都会回收它的内存。不过,由于垃圾回收器是一个优先级很低的线程,因此不一定会很快发现那些只具有弱引用的对象。

虚引用:

  “虚引用”顾名思义,就是形同虚设,与其他几种引用都不同,虚引用并不会决定对象的生命周期。如果一个对象仅持有虚引用,那么它就和没有任何引用一样,在任何时候都可能被垃圾回收器回收。

虚引用主要用来跟踪对象被垃圾回收器回收的活动。

5.Java对象在内存中的3种状态
(1)可达的:

  Java对象被创建后,如果被一个或多个变量引用,那就是可达的。即从根节点可以触及到这个对象。

  其实就是从根节点扫描,只要这个对象在引用链中,那就是可触及的。

(2)可恢复的:

  Java对象不再被任何变量引用就进入了可恢复状态。

  在回收该对象之前,该对象的finalize()方法进行资源清理。如果在finalize()方法中重新让变量引用该对象,则该对象再次变为可达状态,否则该对象进入不可达状态

(3)不可达的:

  Java对象不被任何变量引用,且系统在调用对象的finalize()方法后依然没有使该对象变成可达状态(该对象依然没有被变量引用),那么该对象将变成不可达状态。

  当Java对象处于不可达状态时,系统才会真正回收该对象所占有的资源。

-----------------------------------OK,下面开始正式讲垃圾回收---------------------------------------------
GC垃圾回收
现在有4个问题

1.什么是垃圾回收?
2.如何判断对象是否可回收?
3.用什么方法回收?
4.什么时候回收?
1.什么是垃圾回收?
在java中,程序员是不需要显示的去释放一个对象的内存的,而是由虚拟机自行执行。在JVM中,有一个垃圾回收线程,它是低优先级的,在正常情况下是不会执行的,只有在虚拟机空闲或者当前堆内存不足时,才会触发执行,扫面那些没有被任何引用的对象,并将它们添加到要回收的集合中,进行回收。

2.如何判断对象是否可回收?
2.1引用计数算法:
概念:

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

但是:

  主流的java虚拟机并没有选用引用计数算法来管理内存,其中最主要的原因是:它很难解决对象之间相互循环引用的问题。

优点:

  算法的实现简单,判定效率也高,大部分情况下是一个不错的算法。很多地方应用到它

缺点:

引用和去引用伴随加法和减法,影响性能

致命的缺陷:对于循环引用的对象无法进行回收

2.2可达性算法:(jvm采用的算法)
概念:

这个算法的基本思路就是通过一系列的称谓“GC Roots”的对象作为起始点,从这些节点开始向下搜索,搜索走过的路径称为引用链,当一个对象到 GC Roots 没有任何引用链相连时,则证明此对象是不可用的。

可达性分析:



  从根(GC Roots)的对象作为起始点,开始向下搜索,搜索所走过的路径称为“引用链”,当一个对象到GC Roots没有任何引用链相连(用图论的概念来讲,就是从GC Roots到这个对象不可达)时,则证明此对象是不可用的。

2.2.1什么可以当GC Roots?
在Java语言中,可作为GC Roots的对象包括下面几种:
1.虚拟机栈(栈帧中本地变量表)中引用的对象。

2.方法区中类静态属性引用的对象。

3.方法区中常量引用的对象。

4.本地方法栈中JNI(即一般说的Native方法)引用的对象。

3.用什么方法回收?
这里分为【垃圾方法】和 【垃圾收集器】

垃圾收集算法是内存回收的【方法论】,垃圾收集器就是内存回收的【具体实现】

3.1 垃圾收集算法
图都是自己用EXCEL画的,如果有错误,请指出。

3.1.1 标记-清除算法(Mark-Sweep)


概念:

分为“标记”和“清除”两个阶段

缺点:

1、标记和清除的过程效率不高(标记和清除都需要从头遍历到尾)

2、标记清除后会产生大量不连续的碎片。

3.1.2 复制算法(Copying)


概念:

将原有的内存空间分为两块,每次只使用其中一块,在垃圾回收时,将正在使用的内存中的存活对象复制到未使用的内存块中,然后清除正在使用的内存块中的所有对象。

适用于对象存活率低的场景(新生代)

优点:

这样使得每次都是对整个半区进行回收,内存分配时也就不用考虑内存碎片等情况

只要移动堆顶指针,按顺序分配内存即可,实现简单,运行效率高

缺点:空间的浪费

3.1.3 标记-整理算法(Mark-Compact)


概念:

标记过程任然与“标记-清除”算法一样,但后续步骤不是直接对可回收对象进行清理,而是让所有存活的对象都向一端移动,然后直接清理掉端边界以外的内存。

适用于对象存活率高的场景(老年代)

优点:

  不会产生内存碎片。

缺点:

  在标记的基础之上还需要进行对象的移动,成本相对较高,效率也不高。

3.1.4 分代收集算法(Generational Collection)
概念:

当前商业虚拟机的GC都是采用的“分代收集算法”,这并不是什么新的思想,只是根据对象的存活周期的不同将内存划分为几块儿。一般是把Java堆分为新生代和老年代:短命对象归为【新生代】,长命对象归为【老年代】。

新生代:

每次垃圾收集时都发现有大批对象死去,只有少量存活,那就选用【复制算法】,只需要付出少量存活对象的复制成本就可以完成收集。

老年代:

老年代中因为对象存活率高、没有额外空间对它就行分配担保,就必须使用“标记-清理”或者“标记-整理”算法来进行回收。

3.2垃圾收集器
垃圾收集算法是内存回收的【方法论】,垃圾收集器就是内存回收的【具体实现】

Serial 收集器

ParNew 收集器

Parallel Scavenge收集器

CMS收集器(Concurrent MarkSweep)

G1收集器(Garbage-First)

// TODO 补充各个收集器原理

4.垃圾回收动作何时执行?
当年轻代内存满时,会引发一次普通GC,该GC仅回收年轻代。需要强调的时,年轻代满是指Eden代满,Survivor满不会引发GC

当年老代满时会引发Full GC,Full GC将会同时回收年轻代、年老代

当永久代满时也会引发Full GC,会导致Class、Method元信息的卸载

发帖前要善用论坛搜索功能,那里可能会有你要找的答案或者已经有人发布过相同内容了,请勿重复发帖。

您需要登录后才可以回帖 登录 | 注册[Register]

本版积分规则

返回列表

RSS订阅|小黑屋|处罚记录|联系我们|吾爱破解 - LCG - LSG ( 京ICP备16042023号 | 京公网安备 11010502030087号 )

GMT+8, 2024-11-25 11:39

Powered by Discuz!

Copyright © 2001-2020, Tencent Cloud.

快速回复 返回顶部 返回列表