zoukankan      html  css  js  c++  java
  • JAVA JVM垃圾回收 JVM调优

    总所周知,Java中垃圾是由JVM自动回收,而不需要程序员自己动手,这样编码难度确实降低了,但是其回收的性能成为问题

    1.什么是垃圾

      没有任何引用指向的一个对象或者多个对象(循环引用)会被JVM认为是垃圾

    2.如何定位垃圾

      1.引用计数法(单个对象):引用的增加和减少都被记录,而引用为0的时候,就认为是垃圾

      2.根可达算法(多个对象):以根为起点顺藤摸瓜,能摸到的都不是垃圾。那么Java中那些被认为是根呢?

        JVMstack:JVM栈里面的(因为栈里面的东西由操作系统自动回收,所有栈里面还有的对象认为是存活的对象)

        native method stack:本地方法栈里面的

        runtime constant pool:常量池里面的

        static:静态的引用

        class:加载到内存中的类(类会在new、getstatic、putstatic、invokestatic时加载到内存中,即new类、获取和设置静态变量、调用静态方法时被加载,加载之后将不会被自动回收,也不再重新加载)

    3.垃圾回收算法

      1.mark sweep:标记清除法,把堆中的所有垃圾标记出来,然后清除,这个简单也粗暴,因为没有重新整理存储空间,会产生很多碎片

      2.copying:拷贝算法,把堆一分为二,存一半,空一半,把非垃圾按顺序copy到空的那一半内存中(内存copy效率非常高),下一次回收时又copy回去。这样不会产生碎片,效率也很高,但是空间浪费

      3.mark compact:标记压缩法,相当于一栋楼里面很多房间,一人住一间,把高楼层的人移动到底楼层来住。从高往底找,找到第一个人就放到最底下的第一个空房间或者废弃房间(垃圾房间),一直找下去。这样也没有碎片,空间也不浪费,但是效率低

    4.JVM份代模型

      JVM把堆内存分为两个大区:新生代和老年代

      1.新生代(只用copying算法回收):新生代又分为eden区、两个survivor区,其比例为 8:1:1,eden区存储新进内存的对象,survivor区开始不存东西。为什么要这么分区,因为java中很多东西生命周期都是很短的,比如for循环,下一次循环时上一次的东西就变成垃圾,再比如方法执行完毕,留下的几乎是垃圾,这些都需要频繁的回收,而且会回收大部分的内容,所以就适合用copying算法,把eden区和survivor2区存活的东西,全部放到survivor1区(放不下的直接去老年代),然后把eden区和survivor2区全部清空;第二次回收时,把eden区和survivor1区存活的东西,全部放到survivor2区,这样就能高效的回收垃圾。这也是YGC(新生代垃圾回收,当eden区满了就进行YGC)的策略。

      2.老年代(用mark sweep和mark compact算法回收):这个区专门装顽固分子,只有三种情况会进入老年代:某次YGC时survivor区装不下;某次YGC移动了大于survivor区总大小的50%;对象的年龄达到某个标准(对象年龄:一个对象经历了一次YGC而没有被清除,则年龄上涨一岁,经历了15次YGC则为15岁,也就步入老年),根据垃圾回收器不同,步入老年的年龄也不一样(CMS为6岁,其他为15岁)。而老年代只能通过FGC(full GC:全局GC,新老代都要GC,当老年代满了就进行FGC)来回收。

      ps:另外eden区,为了防止多线程抢地盘,比如你看上这块区域了,我也看上这块区域了,我们就会抢,一个抢到另一个就会存失败,重新去找地方存;所有eden区会为线程分配独享的空间,自己优先往自己的空间存东西

    5.垃圾回收器

      1.serial:分为serial和serial old,分别运行在新老生代;串行收集器,单线程叫停型垃圾回收器,它是单线程的,工作时会叫停其他所有线程

      2.parallel(注重吞吐量,延长YGC间隔时间):分为parallel scavenge和parallel old,分别运行在新老生代;并行收集器,多线程叫停型垃圾回收器,它是多线程的,可以工作到新生代和老年代,工作时会叫停其他所有线程

      3.CMS:多线程并发型垃圾回收器,这个回收器只能工作在老年代(因为新生代效率高,不需要它),它分为四个步骤:

        1.CMS介入:叫停所有线程,自己介入,找到根,然后其他线程继续运行(时间很短,找根而已)

        2.标记垃圾:根据自己找到的根,并发的去标记垃圾

        3.再叫停所有线程,多线程核对垃圾(我是这样理解:根是会变的,增加的根是否指向要删除的垃圾?所以需要核对一次;而如果有的不是垃圾而后面变成垃圾了(浮动垃圾),这问题不大,大不了下一次再来回收,所以不用核对),叫停时间相对parallel old回收器来说也不长,因为只需要点名道姓的核对垃圾,而不是重头排查垃圾

        4.并发删除,是MarkSweep算法

      4.ParNew(注重相应时间,缩短YGC过程的时间):用在年轻代,parallel的升级版,主要是为了能配合CMS的并行回收(parallel不能配合CMS)

      5.G1:新型垃圾回收器,很牛,逻辑上分区,物理上不分区

      6.ZGC:新型垃圾回收器,更牛,没有分区

      

      ps:1.CMS用很多问题,以至于没有任何一版本的Java默认是CMS回收器,它删除是用MarkSweep算法,那么就会留下很多碎片,当有一个较大的对象存不进来的时候,它就要清理碎片,然而,它竟然是用serial old来清理,也就是说大家全部别干事了,就看着单线程的serial old来清理,内存越大清理时间越长,有的甚至会停几十个小时

        2.Java1.8就能用G1 垃圾回收器了

        3.Serial 几十兆,PS 上百兆 - 几个G,CMS - 20G,G1 - 上百G(200ms - 10ms) ,ZGC - 4T - 16T(JDK13)(10ms - 1ms) 

    6.JVM调优

      借助阿里开源工具:Arthas,用法直接看文档:https://alibaba.github.io/arthas/;这玩意中文文档,而且写得很清楚,又开源好用(能把class热部署,也就是发现一个方法有点写错了,直接把类反编译回来,改了编译好,然后拿去替换掉内存中的类)

    7.内存泄露和内存溢出

      内存溢出 : out of memory 指程序在申请内存时,没有足够的内存空间供其使用

      内存泄露 : memory leak 指程序在申请内存后,无法释放已经申请的内存空间

    8.JVM内存模型

      

      

      

  • 相关阅读:
    Install and Configure OSSEC on Debian 7&8
    手动替换WORDPRESS的GOOGLE字体等加速【非插件】
    LDAP常用命令解析
    Install guide for OpenLDAP and GOsa 2 on Ubuntu & Debian
    Linux 转发 email Redirect local emails to a remote email account
    Ubuntu,忘记了root密码,怎么重置?
    LDAP 原理图解
    CAS实现SSO单点登录原理
    cacti 安装与 与不能显示图像故障解决方案
    LDAP概念和原理
  • 原文地址:https://www.cnblogs.com/bigdatadiary/p/13125810.html
Copyright © 2011-2022 走看看