JVM调优学习笔记整理

1.相关基础知识

1.1 JVM内存模型

参见blog:https://blog.csdn.net/lydon1314/article/details/120716778

网址:https://zhuanlan.zhihu.com/p/101495810
java虚拟机(JVM)的内存主要有五大块组成,分别是虚拟机栈,本地方法栈,程序计数器,方法区和堆,其中前三者是线程私有,后两者是线程所共有,具体模型如下图:
在这里插入图片描述
其各个区域方法的作用各不相同,下边我来详细介绍一下:

1、方法区 (Method Area)

方法区是各个线程共享的内存区域,它用于存储已被虚拟机加载的类信息、常量、静态变量、即时编译器编译后的代码等数据。虽然Java虚拟机规范把方法区描述为堆的一个逻辑部分,但是它却有一个别名叫做 Non-Heap(非堆),目的应该是与 Java 堆区分开来。
一般程序在编译和运行过程中,会把提前加载好的一些信息放入此类接口,程序执行时,各线程会从对应的方法区内取出相应数据。

2.、堆区 (Heap)

Java 虚拟机所管理的内存中最大的一块,Java 堆是所有线程共享的一块内存区域,在虚拟机启动时创建,其中会存放对应的一些实体对象,同时,在回收内存时,也是在此区域清楚未使用以及报废的一些实体对象。
在这里插入图片描述

3、程序计数器 (pc 寄存器)

程序计数器也称为(pc寄存器)是一块较小的内存空间,可以看作是当前线程所执行的字节码的行号指示器。

1,通过线程拿到cpu的执行权,执行程序,当执行完毕以后,释放cpu的执行权

2.保存线程的位置,当前线程执行完毕后,程序计数器根据线程的泣置获取该线程,然后执行

**注意:**程序计数器是唯一一个不会出现QutOfMemoryError的内存区域,它的生命周期随着线程的创建而创建,随着线程的结束而死亡。

4、 Java 虚拟机栈(JVM Stacks)

与程序计数器一样,Java虚拟机栈也是线程私有的,它的生命周期和线程相同,描述的是 Java 方法执行的内存模型。此内存区域的唯一目的就是存放对象实例,几乎所有的对象实例以及数组都在这里分配内存。

Java 堆是垃圾收集器管理的主要区域,因此也被称作GC堆(Garbage Collected Heap).从垃圾回收的角度,由于现在收集器基本都采用分代垃圾收集算法,所以Java堆还可以细分为:新生代和老年代:在细致一点有:Eden空间、From Survivor、To Survivor空间等。进一步划分的目的是更好地回收内存,或者更快地分配内存。
在这里插入图片描述

5.本地方法栈(Native Mthod Stacks)

和虚拟机栈所发挥的作用非常相似,区别是: 虚拟机栈为虚拟机执行 Java 方法 (也就是字节码)服务,而本地方法栈则为虚拟机使用到的 Native 方法服务。 在 HotSpot 虚拟机中和 Java 虚拟机栈合二为一。

本地方法被执行的时候,在本地方法栈也会创建一个栈帧,用于存放该本地方法的局部变量表、操作数栈、动态链接、出口信息。

方法执行完毕后相应的栈帧也会出栈并释放内存空间,也会出现 StackOverFlowError 和 OutOfMemoryError 两种异常。

6.总述

java虚拟机的内存大致可分为五大块,而具体java程序在使用java虚拟机时,会调用大部分进行代码编译操作,并将不同类型的数据放入不同区域中,并实现较好的代码调度,比如java中运行时常量池,其也是方法区的一部分,用于存放编译期生成的各种字面量和符号引用。这样的话就可以很大程度上节约内存,避免重复创建。

1.2 内存管理模型

说到内存管理,那就不得不来说一下Gc机制,java虚拟机将产生的数据对象分为三类:包括新生代,老生代和永久代,其中新生代和老生代基本存储在堆区,我们创建对象时首先会扫描对应的类有无加载的对象,有就直接引用,没有就再新创建对象,并为其分配内存。
后续我们在对象使用完成后,我们会对于死亡的对象进行回收,以节约内存,防止内存溢出、内存泄露问题的发生。较为普遍的回收器有三种,包括Full GC,Major Gc, Minor GC。其各自作用的区域也各不相同,当新生代空间不足时,会执行Minor Gc;当老生代空间(新生代空间不足)不足时,会执行major GC,当Full GC执行时,清理老年代空间时,执行完后,空间依然不足,系统会提示outofmemoryError: java heap space;而当永久代(或老年代)空间不足时,会执行Full GC。
除此外,JVM在清除死亡的对象时,还要判断那些对象是死亡的对象,对于计算机而言,死亡的对象不能简单理解为不再使用的对象,而是应该是“ 不可能再被任何途径使用的对象”。那计算机如何确定一个对象是存活还是死亡呢?这就涉及到了垃圾判断算法,其主要包括引用计数法和可达性分析法。

1、 引用计数法

给对象中添加一个引用计数器,每当有一个地方引用它,计数器就加1;当引用失效,计数器就减1;任何时候计数器为0的对象就是不可能再被使用的。反之,当引用失效时,例如一个对象的某个引用超过了生命周期(出作用域后)或者被设置为一个新值时,则之前被引用的对象的计数器的值就减 1。而那些引用计数为 0 的对象,就可以称之为垃圾,可以被收集。
在这里插入图片描述

2、可达性分析算法

这个算法的基本思想就是通过一系列的称为 “GC Roots” 的对象作为起点,从这些节点开始向下搜索,节点所走过的路径称为引用链,当一个对象到 GC Roots 没有任何引用链相连的话,则证明此对象是不可用的。
在这里插入图片描述

3 引用判断

无论是引用计数算法,还是可达性分析算法,对象是否存货都跟 “引用” (reference)有关,在JDK1.2 之后,引用可分为以下4个

强引用:直接 new ,如 new Object(); 只要这类强引用还在,对象就不会回收
软引用:(SoftReference类)用来描述一些还有用但非必需的对象;当将来发生内存溢出之前,系统会把这些有软引用的对象列入回收范围中进行二次回收,如果这次回收还没有足够的内存,则内存溢出报异常;
弱引用:(WakeReference类) 被弱引用的对象只能生存到下一次垃圾收集发生之前,当垃圾收集器开始工作时,无论内存是否足够,都会对这些对象进行回收。
虚引用(幽灵引用/幻影引用):(PhantomReference类) 一个虚引用的对象,它的唯一目的就是能在这个对象被收集器回收时收到一个系统通知,即一个对象是否有虚引用,完全不会对其生存时间构成影响。

1.3 垃圾清除算法

1.3.1 标记-清除算法

标记-清除(Tracing Collector)算法是最基础的收集算法,为了解决引用计数法的问题而提出。它使用了根集的概念,它分为“标记”和“清除”两个阶段:首先标记出所需回收的对象,在标记完成后统一回收掉所有被标记的对象,它的标记过程其实就是前面的可达性分析法中判定垃圾对象的标记过程。

在这里插入图片描述

1.3.2 标记-整理算法

标记-整理(Compacting Collector)算法标记的过程与“标记-清除”算法中的标记过程一样,但对标记后出的垃圾对象的处理情况有所不同,它不是直接对可回收对象进行清理,而是让所有的对象都向一端移动,然后直接清理掉端边界以外的内存。在基于“标记-整理”算法的收集器的实现中,一般增加句柄和句柄表。
3.3 .4 分代收集算法

分代收集(Generational Collector)算法的将堆内存划分为新生代、老年代和永久代。新生代又被进一步划分为 Eden 和 Survivor 区,其中 Survivor 由 FromSpace(Survivor0)和 ToSpace(Survivor1)组成。所有通过new创建的对象的内存都在堆中分配,其大小可以通过-Xmx和-Xms来控制。分代收集,是基于这样一个事实:不同的对象的生命周期是不一样的。因此,可以将不同生命周期的对象分代,不同的代采取不同的回收算法进行垃圾回收,以便提高回收效率。

在这里插入图片描述

1 .3 .3 复制算法

复制(Copying Collector)算法的提出是为了克服句柄的开销和解决堆碎片的垃圾回收。它将内存按容量分为大小相等的两块,每次只使用其中的一块(对象面),当这一块的内存用完了,就将还存活着的对象复制到另外一块内存上面(空闲面),然后再把已使用过的内存空间一次清理掉。
在这里插入图片描述

1 .3 .4 分代收集算法

分代收集(Generational Collector)算法的将堆内存划分为新生代、老年代和永久代。新生代又被进一步划分为 Eden 和 Survivor 区,其中 Survivor 由 FromSpace(Survivor0)和 ToSpace(Survivor1)组成。所有通过new创建的对象的内存都在堆中分配,其大小可以通过-Xmx和-Xms来控制。分代收集,是基于这样一个事实:不同的对象的生命周期是不一样的。因此,可以将不同生命周期的对象分代,不同的代采取不同的回收算法进行垃圾回收,以便提高回收效率。
在这里插入图片描述

2.JVM调优实例

参见:https://blog.csdn.net/u010372867/article/details/53380036
https://blog.csdn.net/liujun19921020/article/details/100554923
https://www.jianshu.com/p/2c4b091deaa3

2.1 实际场景

1.场景1:内存泄露,某个进程突然cpu飙升,且找不到原因

2.场景2:线程死锁,响应变慢…等等

3.场景3:远程的内存占用过大,服务无法启动

2.2 运用原理解析

调优策略:堆设置调优,GC策略调优,JIT调优

2.3 注意事项

2.4 调优案例总结

3、JVM调优总结

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值