zoukankan      html  css  js  c++  java
  • Java内存结构与垃圾回收

      1、JVM垃圾回收机制和常见算法

        理论上来讲Sun公司还制定了垃圾回收规则而不局限于其实现算法,因此不同厂商生产的虚拟机采用的算法也不尽相同。

        GC(Garbage Collertor)在回收对象前首先必须发现那些无用的对象,如何去发现定位这些无用的对象?常用的搜索算法如下:

        1)引用计数器算法(废弃)‘

            应用计数器算法是个每个对象设置一个计数器,当有地方引用这个对象的时候,计数器+1,当引用计数器失效的时候,计数器-1,当计数器为0的时候,JVM就认为对象不再是被使用,是“垃圾”了。

            引用计数器实现简单,效率高;但是不能解决循环引用问题(A对象引用B对象,B对象又引用A对象,但是A、B对象已不再被任何其他对象引用),同时每次计数器的增加和减少都带来了很多额外的开销,所以在JDK1.1之后,这个算法已经不再使用了。

        2)根搜索算法(使用)

            根搜索算法是通过一些“GC Roots”对象作为起点,从这些节点开始往下搜索,搜索通过的路径称为引用链(Reference Chain),当一个对象没有被GC Roots的引用链连接的时候,说明这个对象是不可用的

          

          GC Roots对象包括:

            a)虚拟机栈(栈帧中的本地变量表)中的引用(应用对象)【Java中万物皆对象,引用、

    属性都可以说是对象】

            b)方法区域中的类静态属性的引用

            c)方法区域中常量的引用

            d)本地方法栈中JNI(Native方法)的引用

          通过上面的算法搜索到无用对象之后,就是回收过程,回收算法如下:

          1)标记——清除算法(Mark-Sweep)(DVM使用的算法)

            标记-清除算法包括两个阶段:"标价"和"清除"。在标记阶段,确定所有要回收的对象,并做标记。清除阶段紧随标记阶段,将标记阶段确定不可用的对象清除。标记-清除算法是基础的收集算法,标记和清除阶段的效率不高,而且清除后会产生大量的不连续空间,这样当程序需要分配大内存对象时,可能无法找到足够的连续空间。 

          2)复制算法(Copying)

            复制算法是把内存分成大小相等的两块,每次使用其中一块,当垃圾回收的时候,把存活的对象复制到另一块上,然后把这块内存整个清理掉。复制算法实现简单,运行效率高 ,但是由于每次只能使用其中的一半,造成内存的利用率不高,。现在的JVM复制方法适用新生代,由于新生代中大部分对象(98%)都是朝生夕死的,所以两块内存的比例不是1:1(大概是8:1)(服务器端)。

          3)标记——整理算法(Mark-Compact)

            标记——整理算法和标记——清除算法一样,首先扫描,标记要清除的对象,把存活对象往内存的一段移动,然后直接回收边界以外的内存。标记——整理算法提高了内存的利用率,并且它适合在收集存活时间较长的老年代

          4)分代收集(Generational Collection)

            分代收集是根据对象的存货时间把内存分为新生代和老年代,根据各个代对象的存货特点,每个代采用不同的垃圾回收算法。新生代采用复制算法,老年代采用标记——整理算法。

            垃圾算法的实现设计大量的程序细节,而且不同的虚拟机平台实现的方法也各不相同。

      2、JVM中的新生代和老年代(Eden空间、两个Survior空间)

          现阶段主流的JVM:Hotspot(Sun公司)和JRockit(BEA公司)  都被Oracle收购

          Sun(HotSpot)的GC方式主要有CMS和G1两种。

          CMS(Concurrent Mark Sweep)是JDK1.4后期版本开始引入的新GC算法,在JDK5和JDK6中得到了进一步改进,它的主要场景是对响应时间的重要性需求较高的应用,并且预期这部分应用能够承受垃圾回收线程和应用线程共享处理器资源,且应用中存在比较多的长生命周期的对象的应用。CMS是用于对Tenured Generation(年老代)的回收,目标是尽量减少应用的暂停时间,减少Full GC发生的几率,利用和应用程序线程并发的垃圾回收线程来标记清除老年代

          JVM在程序运行过程当中,会创建大量的对象,这些对象,大部分是短周期的对象,小部分是长周期的对象,对于短周期的对象,需要频繁地进行垃圾回收以保证无用对象尽早被释放掉,对于长周期对象,不需要频繁的垃圾回收则不用进行频繁的垃圾扫描检测。为解决这种矛盾,Sun JVM的内存管理采用分代的策略。

          1)年轻代(Young Gen):年轻代主要存放新创建的对象,内存大小相对会比较小,垃圾回收时会比较频繁。年轻代分为1个Eden Space和2个Survior Space(命名为A和B)。当对象在堆创建时,将进入年轻代的Eden Space。垃圾回收器进行垃圾回收时,扫描Eden Space和A Survior Space,如果对象仍然存活,则复制到B Survior Space,如果B Survior已经满,则复制到Old Gen。同时,在扫描Survior Space时,如果对象已经经过了几次的扫描任然存活,JVM认为其为一个持久化对象,则将其移到Old Gen。扫描完毕后,JVM将Eden Space和A Survior Space清空,然后交换A和B的角色(即下次垃圾回收时会扫描Eden Space和B Survior Space。这么做主要是为了减少内存碎片的产生。)【空间换时间的方式来加速内存中不在被持有的对象尽快能够得到回收】

          2)年老代(Tenured Gen):年老代主要存放JVM认为生命周期比较长的对象(经过几次的Young Gen的垃圾回收后仍然存在),内存大小相对会比较大,垃圾回收也没那频繁(譬如可能几个小时一次)。年老代采用压缩的方式来避免内存碎片(将存活对象移动到内存片的一边,内存整理)。当然,有些垃圾回收器(譬如CMS垃圾回收器)出于效率的原因,可能不会进行压缩。

          3)持久代(Perm Gen):持久代主要存放类定义,字节码和常量等很少会变更的信息

            Java的GC算法比较成熟,很多其它语言或者框架也都支持GC了,但大多数都是只达到了Java Serial gc这种层面,甚至分generation都为考虑。JDK7里面针对CMS进行了一种改进,会采用一种G1(Garbage-First Garbage Collection)的算法。实际上Garbage-First-paper(PDF)2004年已经出现。

          JVM区域总体分为两类,heap(堆)和非heap区。

          heap区又分:Eden Space(伊甸园)、Survior Space(幸存者区)、Tenured Space(老年代-养老区)。

          非heap区:Code Cache(代码缓存区)、Perm Gen(永久代)、JVM Stack(虚拟机栈)、Local Method Stack(本地方法栈)

          

  • 相关阅读:
    HDU 1017—A Mathematical Curiosity
    N !
    L
    J
    Danganronpa
    A water problem
    hdu 5461 Largest Point
    India and China Origins hdu 5652 (BFS+二分)
    D (多校训练三) poj1919 (二分)
    Discovering Gold lightoj 1030 (dp+期望)
  • 原文地址:https://www.cnblogs.com/scar1et/p/11885364.html
Copyright © 2011-2022 走看看