jvm性能优化

来源:https://www.bilibili.com/video/BV1Ht4y1X7p4?p=2&spm_id_from=pageDrive

https://blog.csdn.net/u012988901/article/details/100630491

运行时数据区分为哪几个部分?在这里插入图片描述

在这里插入图片描述

如上图所示,JVM中的运行时数据区应该包括这些部分。在JVM规范中虽然规定了程序在执行期间运行时数据区应该包括这几部分,但是至于具体如何实现并没有做出规定,不同的虚拟机厂商可以有不同的实现方式。

二.运行时数据区的每部分到底存储了哪些数据?

1.程序计数器

在这里插入图片描述

程序计数器(Program Counter Register),也有称作为PC寄存器。想必学过汇编语言的朋友对程序计数器这个概念并不陌生,在汇编语言中,程序计数器是指CPU中的寄存器,它保存的是程序当前执行的指令的地址(也可以说保存下一条指令的所在存储单元的地址),当CPU需要执行指令时,需要从程序计数器中得到当前需要执行的指令所在存储单元的地址,然后根据得到的地址获取到指令,在得到指令之后,程序计数器便自动加1或者根据转移指针得到下一条指令的地址,如此循环,直至执行完所有的指令。

虽然JVM中的程序计数器并不像汇编语言中的程序计数器一样是物理概念上的CPU寄存器,但是JVM中的程序计数器的功能跟汇编语言中的程序计数器的功能在逻辑上是等同的,也就是说是用来指示 执行哪条指令的。

由于在JVM中,多线程是通过线程轮流切换来获得CPU执行时间的,因此,在任一具体时刻,一个CPU的内核只会执行一条线程中的指令,因此,为了能够使得每个线程都在线程切换后能够恢复在切换之前的程序执行位置,每个线程都需要有自己独立的程序计数器,并且不能互相被干扰,否则就会影响到程序的正常执行次序。因此,可以这么说,程序计数器是每个线程所私有的。

在JVM规范中规定,如果线程执行的是非native方法,则程序计数器中保存的是当前需要执行的指令的地址;如果线程执行的是native方法,则程序计数器中的值是undefined。

由于程序计数器中存储的数据所占空间的大小不会随程序的执行而发生改变,因此,对于程序计数器是不会发生内存溢出现象(OutOfMemory)的。

2.Java栈

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

Java栈也称作虚拟机栈(Java Vitual Machine Stack),也就是我们常常所说的栈,跟C语言的数据段中的栈类似。事实上,Java栈是Java方法执行的内存模型。为什么这么说呢?下面就来解释一下其中的原因。

Java栈中存放的是一个个的栈帧,每个栈帧对应一个被调用的方法,在栈帧中包括局部变量表(Local Variables)、操作数栈(Operand Stack)、指向当前方法所属的类的运行时常量池(运行时常量池的概念在方法区部分会谈到)的引用(Reference to runtime constant pool)、方法返回地址(Return Address)和一些额外的附加信息。当线程执行一个方法时,就会随之创建一个对应的栈帧,并将建立的栈帧压栈。当方法执行完毕之后,便会将栈帧出栈。因此可知,线程当前执行的方法所对应的栈帧必定位于Java栈的顶部。讲到这里,大家就应该会明白为什么 在 使用 递归方法的时候容易导致栈内存溢出的现象了以及为什么栈区的空间不用程序员去管理了(当然在Java中,程序员基本不用关系到内存分配和释放的事情,因为Java有自己的垃圾回收机制),这部分空间的分配和释放都是由系统自动实施的。对于所有的程序设计语言来说,栈这部分空间对程序员来说是不透明的。下图表示了一个Java栈的模型:

局部变量表,顾名思义,想必不用解释大家应该明白它的作用了吧。就是用来存储方法中的局部变量(包括在方法中声明的非静态变量以及函数形参)。对于基本数据类型的变量,则直接存储它的值,对于引用类型的变量,则存的是指向对象的引用。局部变量表的大小在编译器就可以确定其大小了,因此在程序执行期间局部变量表的大小是不会改变的。

操作数栈,想必学过数据结构中的栈的朋友想必对表达式求值问题不会陌生,栈最典型的一个应用就是用来对表达式求值。想想一个线程执行方法的过程中,实际上就是不断执行语句的过程,而归根到底就是进行计算的过程。因此可以这么说,程序中的所有计算过程都是在借助于操作数栈来完成的。

指向运行时常量池的引用(动态连接),因为在方法执行的过程中有可能需要用到类中的常量,所以必须要有一个引用指向运行时常量。

方法返回地址,当一个方法执行完毕之后,要返回之前调用它的地方,因此在栈帧中必须保存一个方法返回地址。

由于每个线程正在执行的方法可能不同,因此每个线程都会有一个自己的Java栈,互不干扰。

3.本地方法栈、

在这里插入图片描述

本地方法栈与Java栈的作用和原理非常相似。区别只不过是Java栈是为执行Java方法服务的,而本地方法栈则是为执行本地方法(Native Method)服务的。在JVM规范中,并没有对本地方发展的具体实现方法以及数据结构作强制规定,虚拟机可以自由实现它。在HotSopt虚拟机中直接就把本地方法栈和Java栈合二为一。

5.方法区

方法区在JVM中也是一个非常重要的区域,它与堆一样,是被线程共享的区域。在方法区中,存储了每个类的信息(包括类的名称、方法信息、字段信息)、静态变量、常量以及编译器编译后的代码等。

在Class文件中除了类的字段、方法、接口等描述信息外,还有一项信息是常量池,用来存储编译期间生成的字面量和符号引用。

在方法区中有一个非常重要的部分就是运行时常量池,它是每一个类或接口的常量池的运行时表示形式,在类和接口被加载到JVM后,对应的运行时常量池就被创建出来。当然并非Class文件常量池中的内容才能进入运行时常量池,在运行期间也可将新的常量放入运行时常量池中,比如String的intern方法。

在JVM规范中,没有强制要求方法区必须实现垃圾回收。很多人习惯将方法区称为“永久代”,是因为HotSpot虚拟机以永久代来实现方法区,从而JVM的垃圾收集器可以像管理堆区一样管理这部分区域,从而不需要专门为这部分设计垃圾回收机制。不过自从JDK7之后,Hotspot虚拟机便将运行时常量池从永久代移除了。

什么是jvm:

在这里插入图片描述

在这里插入图片描述

栈(线程):每个线程都会拥有一个方法调用栈(线程栈),用来跟踪线程运行中(一旦虚拟机开始运行,栈就会给线程分配一块独立的运行空间,用来存放线程的局部变量,)

的一系列方法调用过程,栈中的每一个元素被称为栈帧,每当线程调用一个方法的时候会

向方法栈中压入一个新帧。这里的帧用来存储方法的参数、局部变量、方法的返回地址、

和运算过程中的临时数据。

此可以看出方法调用和线程启动的区别。方法调用只是在原来的线程栈中调用方法的代码,而线程启动会新建立一个独属于自己的线程栈来运行自己的这个线程。

可以参考:https://www.cnblogs.com/lzq210288246/p/13067659.html

https://blog.csdn.net/dengruo6708/article/details/102397019?utm_medium=distribute.pc_aggpage_search_result.none-task-blog-2aggregatepagefirst_rank_v2~rank_aggregation-1-102397019.pc_agg_rank_aggregation&utm_term=gc+root%E8%AF%A6%E8%A7%A3&spm=1000.2123.3001.4430

什么叫GCroot

一、可达性分析算法:通过一系列的名为“GC Root”的对象作为起点,从这些节点向下搜索,搜索所走过的路径称为引用链(Reference Chain),当一个对象到GC Root没有任何引用链相连时,则该对象不可达,该对象是不可使用的,垃圾收集器将回收其所占的内存。

在java语言中,可作为GCRoot的对象包括以下几种:

a. java虚拟机栈(栈帧中的本地变量表)中的引用的对象。 

b.方法区中的类静态属性引用的对象。 

c.方法区中的常量引用的对象。 

d.本地方法栈中JNI本地方法的引用对象。

在这里插入图片描述

MinorGC

从年轻代空间(包括 Eden 和 Survivor 区域)回收内存被称为 Minor GC,也叫Young GC。因为Java对象大多具备朝生夕死的特征,所以MinorGC非常频繁,一般回收速度也比较快。一般采用复制算法。

Minor GC触发条件

Eden区域满了
新生对象需要分配到新生代的Eden,当Eden区的内存不够时需要进行MinorGC

在这里插入图片描述
minor gc主要回收的是年轻代
在这里插入图片描述
在这里插入图片描述

图示GC过程
1:初始阶段,对象分配在Eden区(大对象直接进入老年代,通过-XX:PretenureSizeThreshold配置),此时S0和S1是空的(圆圈中的数字代表对象的年龄)
在这里插入图片描述

2:当Eden区满了之后,进行MinorGC,经过扫描与标记,不再存活的对象被清除,存活的对象进入Survivor中的S0并且对象年龄+1,此时Eden被清空,S1是空的
在这里插入图片描述

3:然后随着对象增多又一次MinorGC后,Eden区和S0区存活的对象进入S1区并且对象年龄+1,Eden和S0区被清空

在这里插入图片描述

4:又一次MinorGC后,和上面步骤类似,Eden区和S1区存活的对象进入S0区并且对象年龄+1,Eden和S1区被清空

在这里插入图片描述

5:对象每熬过一次MinorGC其年龄就会加1,达到年龄阈值(可通过参数-XX:MaxTenuringThreshold配置)的年轻代对象会晋升到老年代,随着进入老年代的对象越来越多,当老年代内存不够用时会发送MajorGC。

在这里插入图片描述

底层垃圾回收 图
输入cmd ----->jvisualvm
在这里插入图片描述

Minor GC、Major GC和Full GC之间的区别

概念:
● 新生代 GC(Minor GC):从年轻代空间(包括 Eden 和 Survivor 区域)回收内存被称为 Minor GC,因为 Java 对象大多都具备朝生夕灭的特性,所以 Minor GC 非常频繁,一般回收速度也比较快。这一定义既清晰又易于理解。但是,当发生Minor GC事件的时候,有一些有趣的地方需要注意到:

  1. 当 JVM 无法为一个新的对象分配空间时会触发 Minor GC,比如当 Eden 区满了。所以分配率越高,越频繁执行 Minor GC。
  2. 内存池被填满的时候,其中的内容全部会被复制,指针会从0开始跟踪空闲内存。Eden 和 Survivor 区进行了标记和复制操作,取代了经典的标记、扫描、压缩、清理操作。所以 Eden 和 Survivor 区不存在内存碎片。写指针总是停留在所使用内存池的顶部。
  3. 执行 Minor GC 操作时,不会影响到永久代。从永久代到年轻代的引用被当成 GC roots,从年轻代到永久代的引用在标记阶段被直接忽略掉。
  4. 质疑常规的认知,所有的 Minor GC 都会触发“全世界的暂停(stop-the-world)”,停止应用程序的线程。对于大部分应用程序,停顿导致的延迟都是可以忽略不计的。其中的真相就 是,大部分 Eden 区中的对象都能被认为是垃圾,永远也不会被复制到 Survivor 区或者老年代空间。如果正好相反,Eden 区大部分新生对象不符合 GC 条件,Minor GC 执行时暂停的时间将会长很多。
    所以 Minor GC 的情况就相当清楚了——每次 Minor GC 会清理年轻代的内存。
    ● 老年代 GC(Major GC / Full GC):指发生在老年代的 GC,出现了 Major GC,经常会伴随至少一次的 Minor GC(但非绝对的,ParallelScavenge 收集器的收集策略里就有直接进行 Major GC 的策略选择过程) 。MajorGC 的速度一般会比 Minor GC 慢 10倍以上。

Minor GC触发机制:
当年轻代满时就会触发Minor GC,这里的年轻代满指的是Eden代满,Survivor满不会引发GC。
Full GC触发机制:
(1)调用System.gc时,系统建议执行Full GC,但是不必然执行
(2)老年代空间不足
(3)方法区空间不足
(4)通过Minor GC后进入老年代的平均大小大于老年代的可用内存
(5)由Eden区、survivor space1(From Space)区向survivor space2(To Space)区复制时,对象大小大于To Space可用内存,则把该对象转存到老年代,且老年代的可用内存小于该对象大小

当永久代满时也会引发Full GC,会导致Class、Method元信息的卸载。
其中Minor GC如下图所示
在这里插入图片描述
虚拟机给每个对象定义了一个对象年龄(Age)计数器。如果对象在 Eden 出生并经过第一次 Minor GC 后仍然存活,并且能被 Survivor 容纳的话,将被移动到 Survivor 空间中,并将对象年龄设为 1。对象在 Survivor 区中每熬过一次 Minor GC,年龄就增加 1 岁,当它的年龄增加到一定程度(默认为 15 岁)时,就会被晋升到老年代中。对象晋升老年代的年龄阈值,可以通过参数 -XX:MaxTenuringThreshold (阈值)来设置。

MajorGCvsFullGCMajorGCvsFullGC
大家应该注意到,目前,这些术语无论是在 JVM 规范还是在垃圾收集研究论文中都没有正式的定义。但是我们一看就知道这些在我们已经知道的基础之上做出的定义是正确的:
● Major GC 是清理永久代。
● Full GC 是清理整个堆空间—包括年轻代和永久代

stw是什么意思

STW 可以是 Stop the World 的缩写,也可以是 Start the World 的缩写。通常意义上指指代从 Stop the World 这一动作发生时到 Start the World 这一动作发生时这一段时间间隔,即万物静止。STW 在垃圾回收过程中为了保证实现的正确性、防止无止境的内存增长等问题而不可避免的需要停止赋值器进一步操作对象图的一段过程。

在这里插入图片描述

这样的eden:s0:s1=8:1:1 会频繁的触发 full gc
因为 s0 有一条规定 达到s0百分之50的 会直接放到 老年代。,所以,s0中的对象会频繁的把对象放入老年代 没几分钟老年代就满了,老年代一但,满了,就会触发 full gc

在这里插入图片描述
如何jvm调优,让其几乎不触发Full gc ?

在这里插入图片描述
在这里插入图片描述

调整 eden 和 s0 s1 大小的比较,尽量把那些朝生夕死的对象 在年轻代就干掉。 eden区,最后一次放进去的对象,会移动到 s0 中,当下次触发manr Gc 的时候,第14次 方法肯定已经执行完毕(也就是最后一个对象的方法) ,就不会被GC root引用了,所有就会在s0中被manr Gc 回收

在这里插入图片描述

索引的本质

https://blog.csdn.net/hao65103940/article/details/89032538
在这里插入图片描述
想要减少红黑树高度
在这里插入图片描述
hash表可以快速的查找到想要的元素。但是不支持范围的查找。

在这里插入图片描述
在这里插入图片描述
假设一个索引类型为bigint 型占8Bit ,Mysql后跟的指针默认为6bit指向下一个索引。 所以第一层节点可以存储 16kb/14b=1170个索引
在这里插入图片描述
总共索引占8Bit +上存储的数据 =1kb (估算)那么一个叶子节点大概可以存 16个索引元素

在这里插入图片描述
大概存了 分支X分支X分支=1170X170X16 个索引元素
数据库中多用B+tree树这种数据结构
在这里插入图片描述

如何基于索引b+Tree建立高性能索引

在这里插入图片描述
在这里插入图片描述
InnoDB存储引擎索引实现
参考:https://www.cnblogs.com/s-b-b/p/8334593.html
什么是聚集索引?就是索引的叶子节点中包含了完整的数据记录,像innoDB底层的那种实现,
非聚集索引 就是索引的叶子节点,中包含的索引,和数据相分离,不在同一个文件夹下,像MyLSAM存储引擎的实现(如上)
为什么InnoDb表必须有主键,并且推荐使用整形的自增主键?

1.如果设置了主键,那么InnoDB会选择主键作为聚集索引、如果没有显式定义主键,则InnoDB会选择第一个不包含有NULL值的唯一索引作为主键索引、如果也没有这样的唯一索引,则InnoDB会选择内置6字节长的ROWID作为隐含的聚集索引(ROWID随着行记录的写入而主键递增
2. 因为整形的速度比较快。效率高。自增的话,减少B+Tree叶子节点分裂,使用自增会在叶子的节点后,依次增加,如不使用自增,根据B+tree的特点,增加一个数据那个叶子节点存不下了,他会分裂,并把这个元素插入。(这样降低了效率)
在这里插入图片描述

联合索引的底层存储结构长什么样子?

在这里插入图片描述

JVM整体架构

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
栈帧中包含了哪些元素

在这里插入图片描述
在这里插入图片描述
方法出口就是记录的这个方法执行完后该去到那个位置
在这里插入图片描述

======================

在这里插入图片描述
类元空间
在这里插入图片描述
在这里插入图片描述

jvm的内存的分配

在这里插入图片描述
manner gc主要回收的是没有人引用的对象
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

什么是jvm调优?

在这里插入图片描述

如何调优?
在这里插入图片描述

多核并发缓存架构

在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

在这里插入图片描述
早期使用的是总线枷锁,

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

在这里插入图片描述当一批对象的总大小大于等于这块survivor区域内存大小的50%,那么此时大于等于大于等于这批对象年龄最大值的对象。就可以进入老年代了
在这里插入图片描述
在这里插入图片描述

这样就不会达到survivor区域的一半,直接放进老年代,就解决了频繁触发full gc

在这里插入图片描述

https://www.bilibili.com/video/BV1Ht4y1X7p4

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值