A股上市公司传智教育(股票代码 003032)旗下技术交流社区北京昌平校区

 找回密码
 加入黑马

QQ登录

只需一步,快速开始


   我们都知道jvm虚拟机有垃圾回收机制,但是仔细想一下,我们可以总结出以下三个问题:
   一:哪些东西需要回收呢?
   二:什么时候回收呢?
   三:用什么方式回收呢?
   那么弄清上面疑问前,先了解一下jvm的内存分配
   jvm 内存可以分栈,本地方法栈,程序计数器、堆和方法区;
   其中,我们所听说过的,只有栈,堆和方法区。那么这几个内存的分别是啥?
   栈 :线程私有,生命周期和线程同步,存放局部变量(基本数据类型,对象的引用),其所需内存大小编译器时候已经确认,故内存分配有确定性;
   程序计数器: 线程私有,来获取线程下一条执行指令的字节码;
   本地方法栈:执行native服务
   堆:线程共享内存,动态分配,几乎所有的对象实例都在这里分配内存,管理内存最大的一块;
   方法区:线程共享内存,用于存放已经被加载的类的信息、常量、静态变量及编译后的代码等数据;


   了解上面jvm内存分配后,那我们就可以回答第一个问题:
   总结如下:程序计算器、栈、本地方法栈3个区域的内存随着线程而生,随线程而灭。内存分配大体上在编译期间都已经确认,因此这些区域内存分配和回收都有确定性,故就不需要jvm回收了。方法结束或线程结束时,内存自然跟着回收了。而堆和方法区是动态分配的,只有在程序运行期间才知道哪些对象要建,内存分配和回收是动态的,所以,我们关注的垃圾回收只需要了解此部分就可以了。


   那下面我们第二个疑惑:什么时候回收呢?
   其实很简单-----就是东西没有人用了,就可以回收了!那怎么判断东西有没有人再用呢?放在jvm中,假设对象没有线程或方法使用,标记此对象 “挂了”,我们直要把“挂掉”的对象回收就好了,现在就要只道哪些对象已经“挂”了呢?


   判断对象“挂掉”的方法有下面几种:
   第一种算法——引用计数器算法 :给对象添加一个引用计数器,当有引用指向它的时候,计数器就加一;当引用失效时候,计数器就减一。任何计数器为0的时候,就不能再被引用了。此方法简单高效但有个缺点就是两个对象互相引用的时候,GC(Garbage Collection,垃圾回收)就没有办法回收此对象了,故很少采用此算法。


   第二种算法——  可达性分析算法 :目前主流算法,通过可达性分析来判断对象是否活着。其算法思想就是通过GC_root节点作为起始点,往下搜索,搜索走过的路径叫做引用链。当一个对象没有一个GC_ root相连的话,则证明此对象已经没有使用,可以判断可以回收。如下图:object 5,6,7没有GC_root相连,故在回收考虑范围内,但没有直接回收,因为此算法不仅要考虑是否可达,还要考虑另外一个条件:对象的flinalize的方法,其考虑如下:


   可达性分析 没有GC_root 相链的对象,再考虑其对象是否覆盖了Object的flinalize()方法
      若没有————直接回收
      若有————就把把这个对象放在fF-Queue队列,由一低优先级线程执行该队列中对象的finalize方法。执行finalize方法完毕后,GC会再次判断该对象是否可达,若不可达,则进行回收,否则,对象“复活”。所以这种方法对象可以自救。


   


   前两个疑惑解决了,现在解决第三个疑惑,用什么办法回收,现在垃圾收集算法下面几种算法:
   一:标记-清除算法,先标记好,然后统一回收,清除后产生零碎化内存。
   二:复制算法,将可用内存划分为大小相等的两块,每次只使用一块,当这一块内存使用完后,就将还存活的对象复制到另外一块上,然后再把已经用过的内存一次清空。这样每次都对半个内存进行清空,内存分配的时候也不用考虑内存碎片,只要移动栈顶指针就好。压缩清除:为了提升性能,压缩清除会在删除没用的对象后,把所有存活的对象移到一起,这样可以提高分配新对象的效率。
   三:分代收集算法,根据对象存活周期,分为新生代和老年代。针对不同的代采用不同的算法:在新生代中,每次垃圾回收时候,都会发现有大批对象死去,只有少量对象存活,就采用复制算法,只要复制少量的对象就能实现收集。而老生代中,对象存活率高,采用标记-清除方法。


   这里有五种可以在应用里使用的垃圾回收类型。仅需要使用JVM开关就可以在我们的应用里启用垃圾回收策略。让我们一起来逐一了解:


   Serial GC(-XX:+UseSerialGC):Serial GC使用简单的标记、清除、压缩方法对年轻代和年老代进行垃圾回收,即Minor GC和Major GC。Serial GC在client模式(客户端模式)很有用,比如在简单的独立应用和CPU配置较低的机器。这个模式对占有内存较少的应用很管用。


   Parallel GC(-XX:+UseParallelGC):除了会产生N个线程来进行年轻代的垃圾收集外,Parallel GC和Serial GC几乎一样。这里的N是系统CPU的核数。我们可以使用 -XX:ParallelGCThreads=n 这个JVM选项来控制线程数量。并行垃圾收集器也叫throughput收集器。因为它使用了多CPU加快垃圾回收性能。Parallel GC在进行年老代垃圾收集时使用单线程。


   Parallel Old GC(-XX:+UseParallelOldGC):和Parallel GC一样。不同之处,Parallel Old GC在年轻代垃圾收集和年老代垃圾回收时都使用多线程收集。
   并发标记清除(CMS)收集器(-XX:+UseConcMarkSweepGC):CMS收集器也被称为短暂停顿并发收集器。它是对年老代进行垃圾收集的。CMS收集器通过多线程并发进行垃圾回收,尽量减少垃圾收集造成的停顿。CMS收集器对年轻代进行垃圾回收使用的算法和Parallel收集器一样。这个垃圾收集器适用于不能忍受长时间停顿要求快速响应的应用。可使用 -XX:ParallelCMSThreads=n JVM选项来限制CMS收集器的线程数量。


   G1垃圾收集器(-XX:+UseG1GC) G1(Garbage First):垃圾收集器是在Java 7后才可以使用的特性,它的长远目标时代替CMS收集器。G1收集器是一个并行的、并发的和增量式压缩短暂停顿的垃圾收集器。G1收集器和其他的收集器运行方式不一样,不区分年轻代和年老代空间。它把堆空间划分为多个大小相等的区域。当进行垃圾收集时,它会优先收集存活对象较少的区域,因此叫“Garbage First”。你可以在Oracle Garbage-FIrst收集器文档找到更多详细信息。



   以上就是对jvm垃圾回收机制的简单分析,通过上面的分析可以解决了我们上面提到的三个疑,但新的问题又来了,比如一个例子就是:jvm正在打扫,而另一边却在扔垃圾!这应该怎么解决呢,所以推荐一本书《深入理解Java虚拟机:JVM高级特性与最佳实践》,大家不仅可以知道jvm的回收机制,还可以了解jvm的其他特性,为我们以后面试打下良好的基础。

0 个回复

您需要登录后才可以回帖 登录 | 加入黑马