zoukankan      html  css  js  c++  java
  • java虚拟机之垃圾收集器与内存分配策略

      哪些内存需要回收?

        java内存运行时区域的各个部分,其中程序计数器,虚拟机栈,本地方法栈3个区域随线程而生,随线程而灭,栈中的栈帧随着方法的进入和退出而有条不絮的执行着出栈和入栈操作。每一个栈帧中分配多少内存基本上是在类结构确定下来时就已知的。因此这几个区域的内存分配和回收都具有确定性,所以这部分不需要过多考虑内存回收。但是方法区和堆不一样,一个接口中的多个实现类需要的内存可能不一样,一个方法中的多个分支需要的内存也不一样,我们只有在程序处于运行期间才能知道会创建哪些对象,这部分的分配和回收都是动态的,垃圾收集器所关注的就是这部分内存。

      对象对用:

        引用:在jdk1.2之后,引用分为 强引用,软引用,弱引用,虚引用:

          强引用:在程序中普遍存在的,如Object obj=new Object();   这类的引用,只要强引用还在,垃圾收集器永远不会回收掉被引用的对象

          软引用:用来描述一些有用但非必须的对象。在系统将要发生内存溢出异常之前,将会把这些对象列进回收范围之中进行第二次回收。 提供了SoftReference类来实现软引用。

          弱引用:用来描述非必需对象,但是它的强度比软引用更弱一些,被弱引用关联的对象只能生存到下一次垃圾收集发生之前。JVM进行垃圾回收时,无论内存是否充足,都会回收被弱引用关联的对象。在java中,用java.lang.ref.WeakReference类来表示。可以在缓存中使用弱引用。

          虚引用:最弱的引用关系,一个对象是否有虚引用的存在,完全不会对其生存时间构成影响,也无法通过虚引用来取的一个对象实例。为一个对象设置虚引用关联的唯一目的就是能在这个对象被收集器回收时受到一个系统通知。 提供了PhantomReference类来实现虚引用。

      

      回收方法区:

        很多人认为方法区(hotspot称永久代)不需要垃圾收集,而且java虚拟机规范中确实说过可以不要求虚拟机在方法区实现垃圾收集,而且在方法区中进行垃圾收集的性价比一般比较低。

        垃圾回收主要回收两部分:废弃常量和无用的类。

        回收废弃常量和回收java堆中的对象很相似,如“abc”已经进入常量池,但是系统中没有任何一个String对象叫做“abc”,没有地方引用常量池中的“abc”,这个“abc”就会被清理。常量池中的其他类(接口)、方法、字段的符号引用也类似。

        回收无用的类:类需要同时满足下面3个条件才能算是无用的类(无用的类可以被回收)(在大量使用反射,动态代理,动态生成jsp,频繁定义ClassLoader的场景都需要虚拟机具备类卸载功能,以保证永久代不会溢出):

          1、该类所有的实例都已经被回收

          2、加载该类的ClassLoader已经被回收

          3、加载对应的java.lang.Class对象没有任何地方被引用,无法在任何地方通过反射访问该类的方法。

      垃圾收集算法:

        1、标记-清除算法

          首先需要标记所有需要回收的对象,在标记完成后统一回收所有被标记的对象。标记过程一般采用可达性分析算法,而不是引用计数算法。  它主要有两点不足:1是效率问题,标记和清除两个过程的效率都不高;另一个是空间问题,标记清除之后会产生大量不连续的内存碎片,内存碎片太多可能会导致以后在程序运行时需要分配

        

        

        2、复制算法(解决效率问题)

          复制算法将可用内存按容量划分为大小相等的两块,每次只使用其中一块,当这一块的内存用完了,就将还存活的对象复制到另外一块上面,然后再把已使用过的内存空间一次清理掉。这样使得每次都是整个半区进行内存回收,内存分配时也就不用考虑内存碎片等复杂情况。当内存存活率较多的复制操作,效率会变低,需要更多的空间应对所有对象都100%存活的极端情况,所以老年代一般不能直接采用这种算法。

          IBM公司研究,新生代中的对象98%是朝生夕死,不需要1:1划分内存空间,而将内存分为一块较大的Eden空间和两块较小的Survivor空间。每次使用Eden和其中一块Survivor。当回收时,将Eden和Survivor中还存活着的对象一次性地复制到另外一块Survivor空间上,最后清理掉Eden和使用过的Survivor空间。而HotSpot虚拟机默认Eden和Survivor的大小比例是8:1:1,我们没有办法保证每次回收都只有不多于10%的对象存活,当Survivor空间不够时,需要依赖其他内存进行分配担保。

        

        3、标记整理算法

          标记后,不是直接对可收回对象进行清理,而是让所有存活的对象都向一端移动,然后直接清理掉端边界以外的内存。

          

        4、分代收集算法

        一般把java堆分成新生代和老生代,这样就可以根据各个年代的特点采用最适当的算法。在新生代中选择复制算法,只需要付出少量存活对象的复制成本就可以完成手机。而老生代就必须使用“标记-清理”、“标记-整理”算法来进行回收。

        5、可达性分析法

        从 GC Roots 开始向下搜索,搜索所走过的路径称为引用链。当一个对象到 GC Roots 没有任何引用链相连时,则证明此对象是不可用的,那么虚拟机就判断是可回收对象。

      

  • 相关阅读:
    pandas 流式导出excel
    django serializer 定制error_message
    selenium etree xpath使用总结
    Python之路--Python基础
    初见Flask
    Git
    MySQL补充——索引,流程控制,数据备份,python操作mysql,SQLAlchemy
    Python之路--Django--Ajax、同源策略、Jsonp、CORS
    Python之路--Django--form组件与model form组件
    Python之路--Django--中间件
  • 原文地址:https://www.cnblogs.com/yaohuiqin/p/9576915.html
Copyright © 2011-2022 走看看