(六)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 -- 用来预测抛异常

posted on 2021-05-06 16:11  奇天异下  阅读(93)  评论(0编辑  收藏  举报

导航