JVM的垃圾回收

转载请注明:http://blog.csdn.net/HEL_WOR/article/details/50422622

有一周没写博客了,网上关于JVM垃圾回收的博客资料很多,有概述的,也有描述代码的,或许我看了点书,看了点博客,对垃圾回收的原理可能理解了,晓得它是怎么完成任务的,但我想要一个个的试着实现才能真正理解吧,希望在足够深入后能够触内旁通。

如果有读者发现内容中有错误,欢迎指出,我会吸收改正,在这之前我也尽量做到不出错

在JVM中的内存结构,在深入理解JAVA虚拟机和一些博客上都有比较详细的描述,JAVA内存结构划分见下图:
这里写图片描述

Java的内存划分主要为5个区域,虚拟机栈,本地方法栈,程序计数器都是每个线程一个,在线程执行结束后,这三个部分的内容都是会被直接销毁的,举一个例子,莫枢之前在知乎上的一个回答,垃圾回收机制会回收它眼中的垃圾,但如果你的屋子里放的都是宝贝,他就不知道该不该回收了。可能有点牵强,但这三部分的内容,在拥有它们的线程执行结束被销毁后,作为这个线程的私人物品的这部分内容是一定不会再被使用的,所以垃圾回收器会直接将他们回收。

这里可以随便扯远点,类加载器加载的一些信息,比如class文件内的信息(比如常量池),类的成员,某个类中xxx偏移量上的对象是否引用类型的信息都在放在方法区中的,方法区也被在内存划分中划分为永久代(Permanent Generation),后面点的JDK版本把JAVA垃圾回收器的处理范围延伸到了永久代,永久代内的垃圾一般不会被回收,而且由于类的加载机制,第三方的类信息也会被放入到方法区,所有时常会造成永久区的内存溢出,字符串常量池在JDK8开始已经被移除方法区,放在JAVA Heap中。

虚拟机栈是线程私有,每个线程运行的数据放在虚拟机栈中,每个方法对于对应一个栈帧,每个方法所需要的参数,参数类型都放在局部变量表中,在每个方法结束后对应的栈帧在虚拟机中的出栈入栈,在方法返回时返回地址被压至栈顶,程序计数器保存这个值,线程读取程序计数器中的地址完成方法的跳转。

垃圾回收器对垃圾的回收主要是对Heap和方法区的回收,在这两个区域里,因为是程序共享的,对于线程A是垃圾的数据对线程B而言可能是宝贝,所以垃圾回收器不能如同回收另外3个线程私有的区域一样全盘回收。垃圾回收器把这两个区域按年龄来划分为三代,新生代,老年代,永久代,新生代和老年代在Heap中,永久代在方法区中。

接下来描述在新生代中为什么又要把内存划分为3个区域。这里就要涉及到垃圾回收的算法了,Copy算法和Mark-Sweep算法。
在新生代中使用的是Copy算法,为什么在新生代中使用Copy算法而不使用Mark-Sweep算法,两种算法都能让内存空间规整(MarkSweep算法本身会造成堆碎块,必须再进行压缩操作才能完成内存规整),并且Copy算法为了达到搬移的目的需要拆分一部分内存空间出来作为搬移后的存放区。
因为Mark-Sweep算法的一个主要过程Mark需要保持一段时间内引用关系不发生改变,就如同在深入理解Java虚拟机中举的一个例子,你不能在你妈妈一边在打扫房间时你一边往房间地上扔纸。所以在之前的JDK版本中执行Mark-Sweep算法时,垃圾回收机制会申请中断其他线程,等待Mark-Sweep执行结束后再唤醒其他线程,所以这段时间内你的计算机会失去响应,这也被成为Stop The World,在对象引用关系不断新增和消失的新生代里,如果考虑到吞吐量,是不会使用Mark-Sweep算法的。

对于Copy算法,在HotSpot中将新生代分割为3个区域,Eden(伊甸区),Survive From区,Survive To区,对应空间比例为8:1:1,平时的使用的是Eden区和From区,To区空闲,在发生新生代内存不足不足以进行分配时,会触发Minor GC(增量式GC),HotSpot 的单线程GC使用的是Cheney算法。当触发了Minor GC后,垃圾回收器会将把有引用关系的对象搬移到To区,然后将Eden区和To区清空,但是To区可能也会由于新搬移过来的对象造成内存溢出,需要要对To区内的对象进行Promotion(提升),将To区年龄最老的一部分对象提升到老年区中(JVM会为新生代中的对象维护其年龄,每经历一次minor GC年龄就增加1,达到一定年龄会被提升到老年代。),逻辑如下图:
这里写图片描述

说到垃圾回收算法,在之前自己实现的Redis字典里。有一段代码:

 int hashKey = HashTable.DictGenHashFunction(key);
            HashEntry temp = ht[0].hashEntryArr[hashKey & ht[0].sizemask];
            HashEntry lastEntry = null;
            while (temp != null)
            {
                if (temp.key == key)
                {
                    //// 如果是第一个
                    if (count == 0) 
                    {
                        ht[0].hashEntryArr[hashKey & ht[0].sizemask] = temp.next;

                        //// 处理引用关系,使其能被GC回收
                        temp.next = null;
                        removeFlag = true;
                        break;
                    }

                    if (temp.next == null) 

当时特别描述了处理引用关系,使其能被GC回收,因为在代码里temp实际描述的一个链表,如果不对每个对象的next指针做至null处理,比如:

A.next = B;
B.next = C;

如果是以引用计数器来实现垃圾回收的话,从这段代码上来看,B的引用计数为1,C的引用计数为1,那么B,C这两个对象是不会被当做垃圾回收的,因此只要没有对链表中的每一个对象的next指针做至null处理,就会造成内存泄露。
这里写图片描述
而在JAVA和C#中,GC都没有采用引用计数器的方式来描述一个对象是否为垃圾,因此我那段代码处理是多余的。

既然引用关系是相互的,是变化的,那么我们能不能跳出这个圈找个不变的标准来描述引用关系?所以在Java和C#中对内存垃圾的描述是通过根枚举的方式完成的,本质还是判断是否有应用,但不变的标准变成了一系列根节点,枚举每个根节点,从根节点依次扫描有引用关系的每个节点,这些被找到的节点就是我们需要的宝贝,再把这些节点搬移到To区,没有被找到的节点就是我们需要释放的垃圾,这样就解决的了上面的B,C的问题。这就是Cheney算法的实现思路。

对于Cheney算法,我做了张图,这张图其实把算法的整个运行过程和运行结果都描述得比较清楚了。
这里写图片描述

在另一篇博客从Cheney算法->广度优先搜索->倒酒问题(JAVA实现)有对Cheney算法的描述和一些资料,有兴趣的话可以看看。

对于老年代,在Hot Spot中使用的Mark-Sweep算法,对于其大致逻辑和实现demo,资料在MarkSweep算法也有描述。

后面继续更吧。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
JVM垃圾回收是Java虚拟机的一个重要功能,它的知识体系包括以下内容: 1. 垃圾回收算法:垃圾回收算法是指垃圾回收器采用的垃圾回收策略。常见的垃圾回收算法包括标记-清除、复制、标记-压缩和分代等。 2. 垃圾回收器:垃圾回收器是JVM的一个组成部分,它负责执行垃圾回收算法,并回收Java对象的内存空间。常见的垃圾回收器包括Serial、Parallel、CMS、G1等。 3. 对象存活判定:垃圾回收器需要判断哪些Java对象是可以回收的,哪些是必须保留的。存活对象可以通过引用计数、可达性分析等方式判断。 4. 垃圾回收的过程:垃圾回收的过程包括标记、清除、整理等阶段。标记阶段是找到存活对象的过程,清除阶段是回收无用对象的过程,整理阶段是将存活对象移动到一块连续的内存区域中。 5. 垃圾回收的性能优化:垃圾回收的性能优化包括分代、增量、并发、预处理等技术。分代是指将Java对象分为年轻代和老年代,以便针对不同的对象有针对性地进行垃圾回收。 6. 垃圾回收的调优和监控:垃圾回收的调优和监控可以通过调整堆大小、设置垃圾回收器类型、调整垃圾回收的参数等方式实现。 JVM垃圾回收是Java虚拟机的一个重要功能,掌握其知识体系可以帮助程序员更好地理解Java程序的内存模型和垃圾回收机制,从而编写出高效、可靠的Java程序。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值