• (六)JVM虚拟机


    1、概述

    jvm,java虚拟机,模拟计算机的各项功能,将编译后的java字节码解释成具体平台的机器指令进行运行,从而达到“一次编译,到处运行”;

    流程:java编译器将java代码编译成字节码class文件,类加载器依靠java类库对字节码进行 加载、验证、准备、解析、初始化、使用、卸载等操作;

      加载:将创建一个字节数组,读取字节码文件;

      验证:格式、字节码、元数据、符号饮用的验证;

      准备:为静态变量分配空间并设置默认值;

      解析:对 对象对符号饮用替换为指针直接引用;

      初始化:初始化父类或者执行初始化语句;

    加载器:启动类加载器/扩展类加载器/应用程序类加载器;--将字节码文件加载到内存中,转化成对象;

    双亲委派模型:收到类加载请求,加载器会先请求父类加载器去尝试加载,父类搜索不到,子类才加载;---防止不同加载器加载多次,出现多份同样的字节码文件;

      打破双亲委派:继承ClassLoader类,重写loadClass和findClass;

    2、jvm模型结构

    方法区和堆是所有java线程共享的;方法区:类结构信息,静态变量、构造函数、常量池;  堆:最主要的工作内存区域,存放几乎所有的java对象;

    java栈/jvm栈:是线程的私有的工作内存空间,每当创建线程时,就会为其分配一个工作内存空间;存放基本数据类型、对象的引用;java方法;

    本地方法栈:java方法有时会调用本地方法、native方法;线程的setPriority是java写的,但是实现调用的是C语言实现的,在本地方法栈中;

    程序计数器:线程私有,用于记录线程执行的指令地址,便于上下文切换时恢复到原有状态位置;

    jvm栈,

      有后进先出的栈帧队列,每调用一个函数都会对应入栈,执行完毕出栈,当栈桢队列深度超出jvm允许最大深度时抛StackOverflowError,一般是 递归中会发生;

      jvm栈是可以动态扩展的,当内存不足时,新线程无法分配到内存空间,则抛出OutOfMemory,一般是 创建过多线程会发生;

    3、新生代与老年代

    堆中存放着几乎所有的对象,通过回收机制,自动清理垃圾对象,实现自动管理;

    新生代分为:eden、survivor0、survivor1:

    GC:新生代的eden空间存满时触发Young GC(Minor GC--对象年龄+1;清理新生代的对象到survivor,survivor占用率会升高,eden+s0>s1则放到老年代,老年代剩余空间不足则直接Full GC);老年代空间存满时触发Full GC(Major GC--清理整个堆);经过一定次数(15)GC存活下来的新生代对象升级到老年代,大对象直接进入老年代;

    增加survivor可以减少送到老年代对象的数量,进而减少Full GC的发生;Full GC通常会至少会有1次Young GC,且时间是Young GC的十倍以上;

    两个survivor:首次GC,存活对象移到s0,清空eden;二次GC时,将eden和s0的存活对象移到s1;清空eden和s0;这样,保证了存活对象占用连续的内存空间,避免了碎片化;

    –XX:NewRatio        ----新生代与老年代比例,默认是1:2;

    –XX:SurvivorRatio       ----新生代各个区域比例,默认是Edem : from-s0 : to-s1 = 8 : 1 : 1

    -XX:+MaxTenuringThreshold  ---对象被GC/复制次数,默认15

    永久代的变化:
    JDK1.8前:类的元数据如方法数据、方法信息(字节码,栈和变量大小)、运行时常量池、已确定的符号引用和虚方法表等被保存在永久代中;32位为64M,64位为85M,超出会抛OOM;
    JDK1.8及以后:永久代从Java堆中移除,类的元数据直接保存在本地内存区域,称之为元空间;(永久代大小难确定,调优困难,有效避免内存溢出)

    GC发生时确定堆中对象是存活,两种方法:引用计数法、可达性分析法。
    引用计数法:对象上有引用计数器,被引用时计数器+1,使用完毕计数器-1,0时表示不可能再被使用,实现简单,判断高效,但不能解决相互引用问题;
    可达性分析法:以GCRoots对象做起点,搜索引用链上的对象,当一个对象到 GC Roots没有任何引用链时,意味着该对象可以被回收。

    4、GC收集器与收集算法

    收集器:

      Serial收集器:单线程,复制算法;

      ParNew收集器:多线程,复制算法;

      Parallel收集器:年轻代、高吞吐量、多线程,复制算法;

      Parallel Old 收集器:老年代、多线程,标记整理算法;

      CMS收集器:最短回收停顿时间,标记清除算法;--初始标记/并发标记/重新标记,并发清除,大量碎片;

      G1收集器:标记整理算法;--初始标记,并发标记,最终标记,筛选标记,无碎片;

    CMS收集器和G1收集器的区别:

    CMS收集器是老年代的收集器,可以配合新生代的Serial和ParNew收集器一起使用;以最小的停顿时间为目标;标记清除算法;易产生碎片;

    G1收集器收集范围是老年代和新生代,不需要结合其他收集器使用;可预测停顿时间;标记整理算法;进行空间整合,降低了内存空间碎片。

    收集算法:

    复制算法--常用于新生代,复制对象较少,因为存活对象少:

      将内存空间分为两个相等区域,每次只使用一个;当垃圾回收时,将当前区域正在使用对象复制到另一个区域;复制成本小,且空间被整理,无碎片;但需要两倍空间;

    标记整理算法--常用于老年代,老年代存活对象较多,不适合用复制:

      标记+整理,标记要回收的对象,整理移动存活对象到一端,然后清理边界外对象数据; 无碎片,整理耗时;  最耗时、最慢,需要移动对象;

    标记清除算法--常用于老年代:

      标记+清除,标记要回收的对象,统一回收所有被标记的对象;  有碎片,标记耗时;  中等速度,无需移动对象;

    5、引用类型

    强引用:GC时不会被回收,即便是内存不足抛出异常;

    弱引用:时间较短,发现时就会被回收;

    软引用:内存不足时回收;

    虚引用:任何时候都可以被回收,无用;唯一目的就是回收时收到通知;监控对内存地址的引用;

    强引用  >  软引用  >  弱引用  >  虚引用

    持久代:存放类名、方法、常量池信息、对象数组、jvm内部对象等;默认容量是32/64M;JDK8后元空间取代直接放到内存中了,不占虚拟机空间;

    6、参数配置

    调优与监控:top -Hp pid:cpu的耗时与性能;

          jstat:编译、类加载内存、垃圾收集等运行时的状态信息;jstat -class <pid>

          jstack:某时刻的线程快照,查看堆栈信息;jstack -l <pid>

          jdk-bin路径下:jvisualvm/jconsole配置ip和端口;tomcat加监控端口;-- 堆大小/使用/cpu使用/GC活动/新生代老年代/线程数量/vm参数,页面类似win任务管理器

    堆栈:

    -Xmx3550m--最佳1/4内存
    //最大堆大小为3550m
    -Xms3550m --1/64 设置一致,避免堆内存申请伸缩扩容,且满了才触发GC,初期GC减少
    //设置初始堆大小为3550m
    -Xmn2g --3/8
    //设置年轻代大小为2g
    -Xss128k 256kb-1M
    //每个线程的堆栈大小为128k
    -XX:MaxPermSize=16m  --1/64
    //设置持久代大小为16m
    -XX:NewRatio=4 1.3/1.4
    //设置年轻代与年老代的比值
    -XX:SurvivorRatio=4  8.1.1
    //设置年轻代中Eden区与Survivor区的大小比值
    -XX:MaxTenuringThreshold=0 -串行GC有效
    //设置垃圾最大年龄,进入年老代

    垃圾收集器:

    -XX:+UseParallelGC
    //选择垃圾收集器为并行收集器
    -XX:ParallelGCThreads=20
    //配置并行收集器的线程数
    -XX:+UseConcMarkSweepGC 
    //设置年老代为并发收集
    -XX:CMSFullGCsBeforeCompaction=5
    //设置运行多少次GC以后对内存空间进行压缩、整理
    -XX:+UseCMSCompactAtFullCollection
    //打开对年老代的压缩

    辅助信息:

    -XX:+PrintGC
    //输出形式:[GC 118250K->113543K(130112K), 0.0094143 secs][Full GC 121376K->10414K(130112K), 0.0650971 secs]
    -XX:+PrintGCDetails
    //输出形式:[GC [DefNew: 8614K->781K(9088K), 0.0123035 secs] 118250K->113543K(130112K), 0.0124633 secs] [GC [DefNew: 8614K->8614K(9088K), 0.0000665 secs][Tenured: 112761K->10414K(121024K), 0.0433488 secs] 121376K->10414K(130112K), 0.0436268 secs

    内存堆不足java.lang.OutOfMemoryError: Java heap space -- 增加xmx/xms

    内存栈不足java.lang.OutOfMemoryError: unable to create new native thread -- 减小Xss

    内存持久不足java.lang.OutOfMemoryError: PermGen space-- 增加MaxPermSize

    内存溢出java.lang.StackOverflowError -- 业务逻辑,梳理减少递归次数,增加Xss大小

    堆小回收时间长java.lang.OutOfMemoryError: GC overhead limit exceeded -- 用来预测抛异常

  • 相关阅读:
    Upgrading CentOS 6 to CentOS 7
    redis主从同步错误处理
    【linux】free命令中cached和buffers的区别
    服务器TIME_WAIT和CLOSE_WAIT区别及解决方案
    msyql 主从切换
    MySQL主从同步报错1507
    MYSQL SHOW 用法
    NGINX中的proxy_pass和rewrite
    Web服务器Nginx多方位优化策略
    operator重载运算符
  • 原文地址:https://www.cnblogs.com/huasky/p/14735631.html
Copyright © 2020-2023  润新知