zoukankan      html  css  js  c++  java
  • 初识JVM

    1、JVM架构图

     2、类加载器

      1、启动类加载器(根类加载器Bootstrap Class Loader)

        用来加载Java的核心类库(jre/lib/rt.jar)

      2、扩展类加载器(Extension Class Loader

        用来加载Java/lib/ext

      3、系统类加载器(应用程序加载器 System Class Loader)

        加载来自Java命令的-classpath选项、java.class.path系统属性,或者CLASSPATH换将变量所指定的JAR包和类路径

      4、自定义类加载器(略)

    3、双亲委派机制

      简单的说就是一个类加载器在接到加载类请求时,会向上委托给自己的父类加载器加载一直到启动类加载器;如果父类可以完成加载任务,就成功返回;父类无法加载才会自己去加载;如果都无法完成加载时就会抛出ClassNotFound的错误;

    4、Native关键字  -  (修饰的方法都存在本地方法区)

      凡是native修饰的,说明Java的作用范围达不到了,会去调用底层C/C++的库;

      调用本地库接口(JNI)加载本地方法库中的方法;

        JNI作用:扩展Java的使用,融合不同的编程语言为java所用;

    5、程序寄存器

      每个线程都有一个程序计数器,是线程私有的,就是一个指针,指向方法区中的方法字节码(用来存储指向下一条指令的地址,也就是即将要执行的指令代码),在执行引擎读取下一条指令,是一个非常小的内存空间,几乎可以忽略不计;

    6、方法区

      所有线程共享,静态变量、常量、类信息(构造方法、接口定义)、运行时的常量池存在方法中,但是实例变量存在堆内存中,和方法区无关;简单说,所有定义的方法的信息都保存在该区域,此区域属于共享空间;

      static修饰、final修饰、Class、常量池;

    7、栈

      栈内存,主管程序的运行,生命周期和线程同步;线程结束,栈内存也就是释放,对于栈来说,不存在垃圾回收问题;会存在栈溢出问题(StackOverFlowError),如递归调用。。。

    8、堆

      这块区域是 JVM 所管理的内存中最大的一块。线程共享,主要是存放对象实例和数组,堆内存的大小是可以调节的;

      堆内存中细分为三个区域:

          

        

         所有对象时在伊甸园区创建的;

    #测试机8G内存
    //
    JVM试图使用的最大内存 long l = Runtime.getRuntime().maxMemory(); // JVM的总内存 long l1 = Runtime.getRuntime().totalMemory(); System.out.println("最大内存:"+l/(double)1024/1024);//1796M System.out.println("JVM的总内存:"+l1/(double)1024/1024);//240M

      默认情况下:分配的总内存是电脑内存的1/4,而初始化的内存是1/64;

      扩展1:遇到OOM(Out Of Memory)该怎么处理?

          1、可以尝试调大VM分配内存:

    #VM OPTIONS命令
    -Xms1024m -Xmx1024m -XX:+PrintGCDetails
        -Xms:设置初始总内存分配大小,默认1/64
        -Xmx:设置最大分配内存,默认1/4
        XX:+PrintGCDetails:打印GC信息

          控制台查看配置后的结果:1.8之后,元空间(之前叫永久代)逻辑上存在,物理上不存在,由下图可以看出; 

           2、调大内存后还是出错,就分析内存快照,看一下哪个地方出现了问题(需要借助专业工具)

            测试工具有JProfiler、MAT等,这边用JProfiler分析Dump内存文件;

            首先,本机安装JProfiler;然后IDEA也要安装扩展,IDEA安装完重启下工具栏就会显示图标了;

             然后,本机安装好后,IDEA还要配置下安装路径;

      也是需要配置下VM参数;

    #VM OPTIONS 为了方便测试,将虚拟机内存调小了,然后指定Dump内存溢出错误时文件
    -Xms1m -Xmx1m -XX:+HeapDumpOnOutOfMemoryError

      运行之后会在项目根目录生成一个.hprof文件,双加开发即可:

    9、GC分代回收算法

      参考链接:很详细清晰;

      GC的作用区域在堆和方法区;

      JVM在进行GC时,并不是对这三个区域统一回收。大部分时候,回收都是新生代:新生代 -》幸存区(分from,to两块,动态变换,空的变为to区)-》老年代

      默认配置当一个对象经历了15次GC,还没有死,就可以进入老年代了;以下参数可以调整进入老年代的次数

    #VM OPTIONS
    -XX:+MaxTenuringThreshold=16

      1、两种GC

        轻GC(普通的GC):

          1)在不断创建对象的过程中,当Eden区域被占满,此时会开始做轻GC;

          2)此时不能回收的被放入幸存To区

          3)如果幸存From区也是空的,那么From区变为To区,To区变为From区;如果幸存From区已存放有对象就复制到To区,此时From区清空再互换(From、To)身份(From和To是逻辑层次的,反正就是幸存区总会有一个是空的状态,称之为To区);

          4)依次类推,始终保证幸存区有一个空的,用来存储临时对象,用于交换空间的目的。反反复复多次没有被淘汰的对象,将会被放入Old区域中,默认15次(由参数--XX:MaxTenuringThreshold=15 决定);

          

        重GC(全局GC/Full GC):

          JVM会安全的暂停所有正在执行的线程,来回收内存空间,在这个时间内,所有除了回收垃圾的线程外,其他有关JAVA的程序,代码都会静止,反映到系统上,就会出现系统响应大幅度变慢,卡机等状态。

          触发Full GC的情况:

            1)System.gc()方法的调用:

    此方法的调用是建议JVM进行Full GC,尽管仅仅是建议而非一定,但非常多情况下它会触发 Full GC,从而添加Full GC的频率,也即添加了间歇性停顿的次数。
    强烈影响系建议能不使用此方法就别使用,让虚拟机自己去管理它的内存,可通过通过-XX:+DisableExplicitGC来禁止RMI调用System.gc。

            2)老年代空间不足

    一种是分配一个对象,空间真的不足。另一种是由于内存碎片,导致没有连续内存空间来分配给对象。

            3)metaspace空间不足也会造成Full GC

    metaspace中存放的为一些class的信息、常量、静态变量等数据,当系统中要载入的类、反射的类和调用的方法较多时,Permanet Generation可能会被占满。在未配置为採用CMS GC的情况下也会执行Full GC。

            4)之前历次minorGC晋升到老年代的对象平均大小如果大于此时老年代的剩余空间,也会导致一次Full GC。

      2、常用算法

        引用计数器:每有一个引用就+1,基本不用;

        复制算法:

          优点:没有内存的碎片;

          缺点:浪费了空间,幸存区永远有一个是空的;假设百分百存活的话,复制起来效率很差;

        标记清除算法:

          优点:不需要额外的空间;

          缺点:两次扫描(标记,清除),严重浪费时间,会产生内存碎片;

        标记压缩:在标记清除两次扫描,再一次扫描,整理内存空间

          优点:防止内存碎片产生;

          缺点:又多一次扫描;

        总结:

    内存效率(时间复杂度) 复制算法 > 标记清除算法 > 标记压缩算法
    内存整齐度 复制算法 == 标记压缩算符 > 标记清除算法
    内存利用率 标记压缩算符 == 标记清除算法 > 复制算法
    年轻代 存活率低,用复制算法
    老年代 区域大,存活率高,标记清除算法 + 标记压缩(混合);调优:设定几次标记清除后再压缩

        

      GC中相关问题

        问题1:怎么定义活着的对象?

          从根引用开始,对象的内部属性可能也是引用,只要能级联到的都被认为是活着的对象。

        问题2:什么是根?

          本地变量引用,操作数栈引用,PC寄存器,本地方法栈引用等这些都是根。

        问题3:对象进入Old区域有什么坏处?

          Old区域一般称为老年代,老年代与新生代不一样。新生代,我们可以认为存活下来的对象很少,而老年代则相反,存活下来的对象很多,所以JVM的堆内存,才是我们通常关注的主战场,因为这里面活着的对象非常多,所以发生一次FULL GC,来找出来所有存活的对象是非常耗时的,因此,我们应该避免FULL GC的发生。

        问题4:S0和S1一般多大,靠什么参数来控制,有什么变化?

          一般来说很小,我们大概知道它与Young差不多相差一倍的比例,设置的参数主要有两个:

    -XX:SurvivorRatio=8
    -XX:InitialSurvivorRatio=8

          第一个参数(-XX:SurvivorRatio)是Eden和Survivous区域比重(注意Survivous一般包含两个区域S0和S1,这里是一个Survivous的大小)。如果将-XX:SurvivorRatio=8设置为8,则说明Eden区域是一个Survivous区的8倍,换句话说S0或S1空间是整个Young空间的1/10,剩余的8/10由Eden区域来使用。

          第二个参数(-XX:InitialSurvivorRatio)是Young/S0的比值,当其设置为8时,表示S0或S1占整个Young空间的1/8(或12.5%)。

        问题5:一个对象每次Minor GC时,活着的对象都会在S0和S1区域转移,讲过MInor GC多少次后,会进入Old区域呢?

          默认是15次,参数设置

    --XX:MaxTenuringThreshold=15

        ,计数器会在对象的头部记录它的交换次数

  • 相关阅读:
    第08组 Alpha冲刺(1/4)
    第08组 团队Git现场编程实战
    第二次结对编程作业
    团队项目-需求分析报告
    团队项目-选题报告
    第一次结对编程作业
    第一次个人编程作业
    第一次实践作业
    互联网协议学习笔记
    【选修建模的小练习】长方形椅子的稳定性探究
  • 原文地址:https://www.cnblogs.com/xp2h/p/12563048.html
Copyright © 2011-2022 走看看