jvm体系详情

一、JVM体系结构

在这里插入图片描述

  • jvm优化99%都是优化方法区和堆

二、类加载器

​ 1.启动类(根)加载器 Bootstrap ClassLoader 默认会去加载JAVA_HOME/lib目录下的jar加载的是核心类

  1. 扩展类加载器 ExtClassLoader /jre/lib/ext 默认会去加载JAVA_HOME/lib/ex目录下的jar 加载的是拓展类
  2. 应用程序加载器 AppClassLoader 比如web应用,会去加载web程序下ClassPath下的类 加载的是应用类
  3. 用户自定义类加载器 User ClassLoader
加载:从字节码加载成二进制流的过程
验证:验证Class文件是否符合虚拟机规范
准备:为静态变量常量赋默认值
解析:把常量池中的符号引用替换为直接引用。符号引用就理解为一个标示,而在直接
引用直接指向内存中的地址。
初始化:执行static代码块和静态变量初始化(如果存在父类先对父类进行初始化)
类加载完成就是要对对象分配空间和初始化的过程
1.首先为对象分配合适大小的内存空间
2.为实例变量赋值
3.设置对象头,hash码、GC分代年龄、元数据等信息
4.执行构造函数(init)初始化
------------------------------------------
比较两个类是否“相等”,只有在两个类是由同一个类加载器加载的前提下才有意义,
否则,即使这两个类来源于同一个Class文件,被同一个Java虚拟机加载,只要
加载它们的类加载器不同,这两个类就一定不相同

三、双亲委派机制

  1. 类加载器收到类加载请求

  2. 将这个请求向上委托给父类加载器去完成,一直向上直到启动类加载器

  3. 启动类加载器若是可以加载直接加载,若是不能则通知子加载器进行加载

    请求:APP->Ext->Root

    加载:Root->Ext->APP

四、Native

  • 凡是带了native关键字的,都是java作用不到的范围,会去调用C或者C++库,进入本地方法栈,调用本地方法接口JNI

  • JNI作用就是扩展java的用途,融合不同的语言为java所用

  • java在内存中专门开辟了一块内存,本地方法栈,登记native方法,在最终执行的时候,加载本地方法库中的方法通过JNI

五、运行时数据区

1.方法区
  • Method Area

  • 1.8之后方法区用于存储已被虚拟机加载的类信息、常量、静态变量、即时编译后的代码等数据。

2.程序计数器
  • Program Counter Register

  • 当前线程所执行的字节码的行号指示器,个人感觉的他就是为多线程准备的,程序计数器是每个线程独有的,所以是线程安全的。它主要用于记录每个线程的执行情况。

3.堆
  • Java Heap
  • Java 虚拟机中内存最大的一块,是被所有线程共享的,几乎所有的对象实例都在这里分配内存;Java堆也叫GC堆,是垃圾收集器管理的主要区域,堆中可以细分为:新生代、老年代;再细致一点,新生代中又分为:Eden Space(伊甸园)、Survivor空间,Survivor空间又分为From区和to区。
4.虚拟机栈
  • Java Virtual Machine Stacks

  • 线程私有,用于存储局部变量表(存储方法参数和局部变量)、操作数栈(局部变量表的变量在这里面操作)、动态链接(用于将符号引用表示的方法转换为实际的方法的直接引用)、方法出口等信息

5.本地方法栈
  • Native Stack

  • 线程私有,登记native本地方法,调用本地方法接口

栈:

  • 栈内存,主管程序的运行,生命周期和线程同步。
  • 里面包括8大基本数据类型+对象引用+实例的方法
  • 运行原理:栈帧。每个被压入栈的方法的引用或者其他都会有一个父帧和子帧,父帧指向上一个方法的栈帧,指向下一个方法栈帧,在栈顶的方法用完先弹出,则其下一个方法的子帧为空,直到栈底的父帧子帧都为空则用完释放缓存。
  • 栈满了:StackOverFlowErrot

六、堆的结构

垃圾回收主要在伊甸园去和养老区,假设内存满了,jdk8以后永久存储区改了个名字(元空间)。

	产生的对象实例会在Edan,如果eden区满了就会触发young gc,回收垃圾,
回收那些没有人引用的对象(GC Root),把eden里的存活的对象,移到S0区
(复制算法);下次额扥又满了触发ygc,就把eden和S0区里面存活的对象,都通过
复制算法转移到S1区,同时清空垃圾对象,下次又ygc会把eden区和S1区里面存活的
对象放到S0区,这样保证每次都有一块survivor区是清空的
三种场景移到老年代:

第一种,当一个对象在年轻代熬过15次垃圾回收就会被认为是长期存活的对象,
转移到老年代里去;

第二种情况,就是ygc的时候,发现存活的对象很多,而幸存区空间少,直接移到老年代;

第三张就是,刚开始创建的对象特别大,这个很大的对象直接就放到老年代。

在这里插入图片描述

处理OOM:

  1. -Xms8m -Xmx8m -XX:+PrintGCDetails更改jvm最大内存和总内存
  2. jprofiler工具

七、垃圾回收

GC算法
  1. 引用计数法:

  2. 复制算法

    • 优点:没有内存碎片
    • 缺点:浪费了内存空间:有一半幸存区空间永远是空的
    • 最佳使用场景:对象存活度较低的时候:新生区
  3. 标记清除法

    • 优点:不需要额外的空间
    • 缺点:因为要遍历所有的GCROOT,清除的过程也要遍历堆中所有的对象,两次扫描效率低下,会产生内存碎片
  4. 标记整理:把存活的对象压到一边

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-IpOERk5q-1662002138502)(https://pic-for-note.oss-cn-hangzhou.aliyuncs.com/pic-for-typora/GC%E7%AE%97%E6%B3%95zongj.png)]

GC就是分代收集算法

对于新生区:存活率低,用复制算法

对于老年区:存活率高,采用标记清楚+标记压缩混合实现

垃圾回收器
  • Serial

    为单线程环境设计且只使用一个线程进行垃圾回收,会暂停所有的用户线程,所以不适合服务器环境。

  • parallel

    多个垃圾收集线程并行工作,此时用户线程也是暂停的,适用于科学计算/大数据处理。

  • CMS(并发垃圾回收器)

    并发标记清楚,用户线程和垃圾收集线程同时执行(不一定是并行,可能交替执行),不需要停顿用户线程,适用于对响应时间有要求的场景。

    优点:并发进行停顿低

    缺点:并发进行CPU压力大,会导致大量碎片

    第一步:初始标记(CMS inital mark) 标记GC ROOT能关联到的对象 此时STW

    第二步:并发标记(CMS concurrent mark )从GCROOTS的直接关联对象开始遍历整个对象图的过程

    第三步:重新标记(CMS remark) 为了修正并发标记期间,因用户程序继续运作而导致标记产生改变的标记 此时STW

    第四步:并发清除(CMS concurrent sweep)清除标记阶段判断死亡的对象

    CMS

  • G1

    image-20220605190320933

    G1

默认的垃圾回收器有哪些

UseSerialGC:开启新生代和老年代的串行垃圾回收器

UseParNewGC:开启新生代的并行的垃圾回收器,而老年代仍是串行,已经不推荐使用

UseParallerGC:新生代和老年代都使用并行垃圾回收器,java8默认使用的垃圾回收器,重点在于,可控制的吞吐量,自适应调节策略。

UseParallelOldGC:新生代和老年代都使用并行垃圾回收器,与UseParallerGC互相激活

UseConcMarkSweepGC:老年代用CMS,年轻代用ParNew

UseSerialOldGC:1.8之后已经不在使用,只有在CMS出故障时候,老年代才会使用Serial Old

UseG1GC

八、JMM

(Java Memory Model)

是一个虚拟的内存模型,数据存储在主内存中,每个线程都有自己的工作内存,拷贝主内存的数据作为变量副本,对数据的操作不能在主内存,都是在自己的工作内存操作,然后刷到主内存。

九、GCRoots

GCRoot

GCRoots是给定一个集合的引用作为根节点,在堆中的数据,凡是可以追溯到GCRoots
的则叫做引用可达,不能追溯到GCRoots的则不可达,被当做垃圾等待回收
//可以作为GCRoots的对象有:
1.虚拟机栈(栈帧中的本地变量表)中引用的对象
2.方法区中的类静态属性引用的对象
3.方法区中常量引用的对象
4.本地方法栈中JNI(Native方法)中引用的对象

十、JVM的参数类型

如何查看JVM参数值

jsp -l查看当前进程编号
jinfo -flags 进程编号

如何查看JVM初始默认值和修改后的值

java -XX:+PrintFlagsInitial//初始值
java -XX:+PrintFlagsfFinal//修改后的值
java -XX:+PrinCommandLineFlags //可以查看当前使用的垃圾回收器
  • 标配参数 java -version,java - help
  • X参数 -Xint:解释执行,-Xcomp:第一次使用就编译成本地代码,-Xmixed:混合模式
  • XX参数
    • -Xms 等价于-XX:InitialHeapSize//初始内存,默认是物理内存的1/64
    • -Xmx 等价于-XX:MaxHeapSize//最大内存,默认是物理内存的1/4
    • -Xss 等价于-XX:ThreadStackSize//单个线程栈的大小,一般512k-1024k
    • -Xmn 设置年轻代大小
    • -XX:MetaspaceSize 设置元空间大小
    • -XX:+PrintGCDetails 打印GC信息
    • -XX:SurvivorRatio 设置Eden区和s0s1区的比列,默认为8,ex:-XX:SurvivorRatio=4,则Eden:s0:s1=4:1:1
    • -XX:NewRatio 设置老年代和年轻代的比列,默认为2,ex:-XX:NewRatio=4,则老年代:年轻代=4:1
    • -XX:MaxTenuringThreshold 设置垃圾最大年龄,默认15
    • -XX:+DisableExplicitGC 禁止运行期显式地调用 System.gc() 来触发fulll GC

十一、强、软、弱、虚引用

在这里插入图片描述

  • 强引用

    Demo demo = new Demo();

    强引用不会被GC掉,即使内存满出现OOM

  • 软引用

    SoftReference

    内存够的时候保留,内存不够的时候会被回收

  • 弱引用

    WeakReference

    只要GC就会被回收

    在读取大量的本地图片时,考虑用一个hashmap来保存图片路径和相应图片对象关联的软引用之间的映射干,内存不足的时候自动释放内存,从而避免OOM

    WeakHashMap

  • 虚引用

    PhantomReference

    引用在被回收的时候需要被放到ReferenceQueue保存一下

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-5MTDGFjv-1662002138504)(https://pic-for-note.oss-cn-hangzhou.aliyuncs.com/pic-for-typora/%E8%99%9A%E5%BC%95%E7%94%A8.png)]

十二、OOM

  • StackOverFlowError
  • OutOfMemoryError:Java heap space
  • OutOfMemoryError:GC overhead limit exceeded

    GC回收时间过长,效率低,用98%的时间却只回收了2%的内存,导致恶性的GC的循环。

  • OutOfMemoryError:Direct buffer memory

    写NIO程序,一些读写本地数据的ByteBuffer方式,使用Native函数库直接分配堆外内存,通过在堆中的DirectByteBuffer对象作为这块内存的引用进行操作,这种方式显著提高性能,避免在java堆和native中来回复制数据。但若是不断分配本地内存,堆内存充足而本地内存用光,就会出现OutOfMemoryError:Direct buffer memory。

OutOfMemoryError Direct buffer memory

  • OutOfMemoryError:unable to create new native thread

    一个进程创建了太多的线程,这个线程数量和操作系统有关

OutOfMemoryError unable to create new native thread

  • OutOfMemoryError:Metaspace

十三、Linux调优

  1. 查看整机性能

    Top

    其中load average:代表系统1分钟5分钟15分钟系统的平均负载值

    uptime:可以直接查看load average

  2. 查看CPU

    vmstat

    vmstat

    mpstat -P ALL 2查看所有cpu核信息

    pidstat -u 1 -p 进程编号每个进程使用CPU的用量分解信息

  3. 查看内存

    free -m应用程序可用内存

    pidstat -p 进程编号 -r 采样间隔秒数

  4. 查看硬盘

    df -h

  5. 查看磁盘IO

    iostat -xdk 2 3两次采样一秒采样3次

    io xdk

    pidstat -d 采样间隔秒数 -p 进程编号

  6. 查看网络IO

    ifstat

14.CPU占用过高分析

第一步:TOP命令找到占用CPU最高的进程

第二步:ps -ef|grep java|grep -v grep或者jps -l,进一步定位

第三步:定位到具体的线程或者代码 ps -mp 进程编号 -o THREAD,tid,time

第四步:jstack 进程编号 | grep tid(16进制线程ID小写英文) -A60

15、JVM调优

首先,JVM调优主要是为了减少STW的时间,也就是减少GC的次数,
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值