快速了解Java虚拟机

Java虚拟机

Java虚拟机(Java Virtual Machine 简称JVM)是运行所有Java程序的抽象计算机,是Java语言的运行环境,它是Java 最具吸引力的特性之一。《JVM - 百度百科》

类加载机制

类加载机制,指的是将Java类加载到JVM内存当中。类的信息存放在方法区,对象存放在堆。类的加载机制,涉及类的生命周期、类加载器、类的加载机制。值得注意的是,比较多问到的“双亲委派模型”。

双亲委派模型:当一个类加载器收到类加载的请求,会把请求委托给父类去完成,如果父类不能完成请求则依次向上委托请求直到顶层类加载器。如果说还是没有完成请求,那么子加载器才会尝试自己去加载。

JVM内存结构

这里要注意,JVM内存结构(Java内存结构)和Java内存模型是由区别的。指的并不是同一种东西,JVM内存结构是和虚拟机相关,Java内存模型是和多线程相关的。

下面几张图展示JVM内存结构:
在这里插入图片描述
在这里插入图片描述在这里插入图片描述在这里插入图片描述
JVM内存结构主要有三大块:堆内存方法区。堆内存是JVM中最大的一块由年轻代老年代组成,而年轻代内存又被分成三部分,Eden空间From Survivor空间To Survivor空间。

内存结构各区域的区别及概念

一、堆(线程共有)
在这里插入图片描述
堆存放由new创建的对象和数组。几乎所有创建的对象都是在堆上分配空间和、存放。也是GC主要管理的区域。堆又分为(新生代)年轻代老年代

新生代分为Eden区、s0区、s1区,s0和s1也被称为fromto区域,他们是两块大小相等并且可以互相角色的空间。

绝大多数情况下,对象首先分配在eden区,在新生代回收后,如果对象还存活,则进入s0s1区,之后每经过一次新生代回收,如果对象存活则它的年龄就加1,对象达到一定的年龄后,则进入老年代。

二、方法区(线程共有)
方法区为线程共享区域,主要存储被虚拟机已加载的类信息、常量、静态变量、即时编译后的代码等数据。

比如类的字段、方法、常量池等。方法区的大小决定系统可以保存多少个类。如果系统定义太多的类,导致方法区溢出。虚拟机同样会抛出内存溢出的错误。方法区可以理解为永久区。

三、虚拟机栈(线程私有)
一般由三部分组成:局部变量表操作数据栈帧数据区

在执行Java每一个方法的时候,都会创建一个“栈帧”,用于储存:局部变量表、操作栈、动态链接、方法出口等信息。每一个方法被调用直至执行完成,就对应着一个“栈帧”在虚拟机栈中从入栈到出栈的过程。

  • 局部变量表:用于报错函数的参数及局部变量
  • 操作数栈:主要保存计算过程的中间结果,同时作为计算过程中的变量临时的存储空间
  • 帧数据区:除了局部变量表和操作数据栈以外,栈还需要一些数据来支持常量池的解析,这里帧数据区保存着访问常量池的指针,方便计程序访问常量池,另外当函数返回或出现异常时卖虚拟机子必须有一个异常处理表,方便发送异常的时候找到异常的代码,因此异常处理表也是帧数据区的一部分。

四、方法栈(线程私有)
方法栈与虚拟机栈相似,但虚拟机栈是为执行Java方法(字节码)服务,而本地方法栈则为虚拟机使用到的Nactive方法服务。

五、程序计数器(线程私有)
Java虚拟机的多线程是通过线程切换和分配处理器时间。多线程看起来好像是一起运行,但实际上在一个时间点只有一个线程在运行,只是切换和运行太快,肉眼看起来好像所有的线程在同时工作。每个线程执行字节码的时候都独立拥有一个“程序计数器”,以确保线程切换后能正确的继续执行上一次执行的位置。

举个栗子: 简单来说其实你可以理解为一个“书签”,你在一个时段需要看多本书的时候,从一本书切换到另一本,使用书签或记号,使得你下次切换回来时能够继续上一次阅读的进度。

虚拟机参数配置

如果想要了解JVM运行时的状态信息或排查故障,那么就需要使用JVM提供的一些系统参数,使用给定的参数对JVM进行调优(如运行时打印相关日志),可以使开发人员更了解应用程序的运行。虚拟机参数配置,多数是对:堆、方法区、栈进行配置。

堆的常见参数配置:

  • -Xms 堆初始空间Size
  • -Xmx 堆最大可用Size
  • -Xmn 新生代堆最大可用Size
  • -XX:SurvivorRatio 设置新生代中eden空间和from/to空间的比例

其他常见参数配置:

  • -XX:+PrintGC 每次触发GC的时候打印相关日志
  • -XX:+UseSerialGC 串行回收垃圾
  • -xx:permSize 方法区(永久代)大小
  • -xx:metaspaceSize 元数据空间大小
  • -XX:+PrintCommandLineFlags 打印出用户或JVM设置的详细的XX参数的名称和值
  • -XX:+HeapDumpOnOutOfMemoryError 当JVM发生OOM时,自动生成dump文件
  • -XX:HeapDumpPath=${目录}生成DUMP文件的路径,也可以指定文件名称

省略其他更多的参数,感兴趣可以"面向百度"学习一波JVM参数。

tips1: 在实际工作中,我们可以直接将初始的堆大小与最大堆大小相等,
这样的好处是可以减少程序运行时垃圾回收次数,从而提高效率。

tips2: 不同的堆分布情况,对系统执行会产生一定的影响,在实际工作中,应该根据系统的特点做出合理的配置,基本策略:尽可能将对象预留在新生代,减少老年代的GC次数。

除了可以设置新生代的绝对大小(-Xmn),可以使用(-XX:NewRatio)设置新生代和老年代的比例 -XX:NewRatio=老年代/新生代

内存溢出

内存移除分为两种:堆溢出 / 栈溢出

堆溢出java.lang.OutOfMemoryError: ... ...,当创建对象时向虚拟机请求分配内存超过了堆的最大内存空间就会出现该错误。

堆溢出解决办法:设置堆内存大小 -Xms -Xmx XX:+HeapDumpOnOutOfMemoryError 省略其他参数…

栈溢出java.lang.StackOverflowError: ... ...,栈内存溢出,多数出现在方法的递归调用

栈溢出解决办法使用-Xss参数设置最大调用大小

内存泄漏

内存泄露是指该内存空间使用完毕之后未回收,在不涉及复杂数据结构的一般情况下,Java 的内存泄露表现为一个内存对象的生命周期超出了程序需要它的时间长度,我们有时也将其称为“对象游离”。

内存溢出与内存泄漏的区别

简单来说:内存溢出本质上是因为内存空间不足引起的,而内存泄漏指的是内存足够的情况下,但是但是没有及时的去释放内存。出现内存泄漏的话,重启就可以解决了。关于内存泄漏的产生的问题的话,可以上百度了解更多。

垃圾回收机制(GC)

Java语言中一个显著的特点就是引入了垃圾回收机制,使c++程序员最头疼的内存管理的问题迎刃而解,它使得Java程序员在编写程序的时候不再需要考虑内存管理。由于有个垃圾回收机制,Java中的对象不再有“作用域”的概念,只有对象的引用才有“作用域”。垃圾回收可以有效的防止内存泄露,有效的使用空闲的内存。

GC的主要作用区域大部分都是在堆,JVM调优其实基本上久是对堆内存参数调优。我们知道GC大部分是对堆进行垃圾回收,那么怎么回收?回收的算法是什么样的机制呢?

GC中分两种:JVM的自身回收机制和手动回收。
手动回收是开发者显示的使用System.gc()通知GC去回收,但不一定会百分百立马去回收。(垃圾回收机制回收时会调用finalize())

public class GcDemo {
	public static void main(String[] args) {
		GcDemo gd = new GcDemo();
		System.gc();
	}
	protected void finalize() throws Throwable {
       System.out.println(" 测试...");
	}
}

finalize
Java技术使用finalize()方法在垃圾收集器将对象从内存中清除出去前,做必要的清理工作。这个方法是由垃圾收集器在确定这个对象没有被引用时对这个对象调用的。它是在Object类中定义的,因此所有的类都继承了它。子类覆盖finalize()方法以整理系统资源或者执行其他清理工作。finalize()方法是在垃圾收集器删除对象之前对这个对象调用的。

fianl、finally、finalize、的区别是什么?
这是我在网上经常看到的一道面试题,简单的说一下它们的一个区别。

  • final:修饰关键词,用于修饰类、变量、方法,被final修饰的类是不被继承的、修饰的方法是不可被重写的、修饰的变量是不可被更改的。
  • finally:是try catch finally 代码块的一部分,finally块的特性是在一般情况下无论catch块是否捕捉到异常都会执行的代码块。所以在代码逻辑中有需要无论发生什么都必须执行的代码,可以放在 finally 块中。
  • finalize:是方法名,它是Object类中定义的,所有的类都继承了该方法。finalize()方法是在垃圾收集器删除对象之前对这个对象调用的。

垃圾回收机制算法(GC算法)

GC需要找到无用的对象才能进行,需要判断出对象是否被引用。判断对象是否存活一般有两种方式:
引用计数:每个对象有一个引用计数属性,新增一个引用时计数加1,引用释放时计数减1,计数为0时可以回收。此方法简单,无法解决对象相互循环引用的问题。
可达性分析(Reachability Analysis):从GC Roots开始向下搜索,搜索所走过的路径称为引用链。当一个对象到GC Roots没有任何引用链相连时,则证明此对象是不可用的。不可达对象。

主要的GC算法如下几种:

一、引用计数法
在这里插入图片描述
简单来说:给对象中添加一个引用计数器,每当有一个地方引用它时,计数器值就加1;当引用失效时,计数器值就减1;任何时刻计数器都为0的对象就是不再被使用的,垃圾收集器将回收该对象使用的内存。

优点:
引用计数收集器可以很快的执行,交织在程序运行中。对程序需要不被长时间打断的实时环境比较有利。

缺点:
无法检测出循环引用。如父对象有一个对子对象的引用,子对象反过来引用父对象。这样,他们的引用计数永远不可能为0.而且每次加减非常浪费内存。

二、标记-清除在这里插入图片描述
标记-清除(Mark-Sweep)算法顾名思义,主要就是两个动作,一个是标记,另一个就是清除。

标记就是根据特定的算法(如:引用计数算法,可达性分析算法等)标出内存中哪些对象可以回收,哪些对象还要继续用。
标记指示回收,那就直接收掉;标记指示对象还能用,那就原地不动留下。
在这里插入图片描述
缺点:
1.标记与清除效率低;
2.清除之后内存会产生大量碎片;
所以碎片这个问题还得处理,怎么处理,看标记-整理算法。

三、复制
“复制”(Copying)的收集算法,它将可用内存按容量划分为大小相等的两块,每次只使用其中的一块。当这一块的内存用完了,就将还存活着的对象复制到另外一块上面,然后再把已使用过的内存空间一次清理掉。

这样使得每次都是对其中的一块进行内存回收,内存分配时也就不用考虑内存碎片等复杂情况,只要移动堆顶指针,按顺序分配内存即可,实现简单,运行高效。只是这种算法的代价是将内存缩小为原来的一半,持续复制长生存期的对象则导致效率降低。在这里插入图片描述
在这里插入图片描述
s0(from区)和s1(to区)将可用内存按容量分成大小相等的两块,每次只使用其中一块,当这块内存使用完了,就将还存活的对象复制到另一块内存上去,然后把使用过的内存空间一次清理掉。这样使得每次都是对其中一块内存进行回收,内存分配时不用考虑内存碎片等复杂情况,只需要移动堆顶指针,按顺序分配内存即可,实现简单,运行高效。
复制算法的缺点显而易见,可使用的内存降为原来一半。
复制算法用于在新生代垃圾回收

举个栗子:
简单来说就是当新进来一个对象A,会分配到eden区,对象A又由eden区分配至s0区,下次从eden区过来的对象只会分配到s0,此时如果s0区又进来对象B,s0存在AB两个对象。此时对象A没有被引用了,那么系统会把对象B迁移到迁移到s1区,然后把还留在s0的对象A回收。如果又有新的对象C从eden区过来,会分配到s1区,然后系统发现对象B没有被引用了,系统把对象C迁移到s0区然后再把对象B回收掉。这个过程中间是不会同时使用s0和s1空间的,这样的过程一直反复,就是复制算法。

四、标记-压缩(标记清除的升级版)
标记压缩法在标记清除基础之上做了优化,把存活的对象压缩到内存一端,而后进行垃圾清理。 (在Java中,老年代使用的就是标记压缩法) 即该算法用于老年代。

复制收集算法在对象存活率较高时就要执行较多的复制操作,效率将会变低。更关键的是,如果不想浪费50%的空间,就需要有额外的空间进行分配担保,以应对被使用的内存中所有对象都100%存活的极端情况,所以在老年代一般不能直接选用这种算法。

根据老年代的特点,有人提出了另外一种“标记-整理”(Mark-Compact)算法,标记过程仍然与“标记-清除”算法一样,但后续步骤不是直接对可回收对象进行清理,而是让所有存活的对象都向一端移动,然后直接清理掉端边界以外的内存
在这里插入图片描述
在这里插入图片描述
举个栗子:
学校是JVM,老师是GC,教室是老年代空间,学生是对象。老师要整理班级,让50分及以下的同学站在教室的左面墙,50分以上的同学站在教室的右面墙。然后老师再把那些站在左面墙的学生通通赶出教室。

五、分代(新生代/老年代)
GC分代的基本假设:绝大部分对象的生命周期都非常短暂,存活时间短。

“分代收集”(Generational Collection)算法,把Java堆分为新生代和老年代,这样就可以根据各个年代的特点采用最适当的收集算法。在新生代中,每次垃圾收集时都发现有大批对象死去,只有少量存活,那就选用复制算法,只需要付出少量存活对象的复制成本就可以完成收集。而老年代中因为对象存活率高、没有额外空间对它进行分配担保,就必须使用“标记-清理”或“标记-整理”算法来进行回收。
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
GC分代的基本假设:绝大部分对象的生命周期都非常短暂,存活时间短。

简单来说:分代算法指的就是新生代和老年代,GC经常去新生代回收,偶尔去老年代回收。

垃圾回收时的停顿现象

垃圾回收的任务是识别和回收垃圾对象进行内存清理,为了让垃圾回收器可以更高效的执行,大部分情况下,会要求系统进如一个停顿的状态。停顿的目的是为了终止所有的应用线程,只有这样的系统才不会有新垃圾的产生。同时停顿保证了系统状态在某一个瞬间的一致性,也有利于更好的标记垃圾对象。因此在垃圾回收时,都会产生应用程序的停顿。

简单来说:就是GC的时候其他线程会Stop The Word,避免GC过程中进来新的对象。所以GC会对应用程序的性能会有一定的影响

垃圾收集器

如果说收集算法是内存回收的方法论,垃圾收集器就是内存回收的具体实现。

在这里插入图片描述
在这里插入图片描述
Serial收集器
Serial收集器(串行收集器)是最古老,最稳定以及效率高的收集器,可能会产生较长的停顿,只使用一个线程去回收。新生代、老年代使用串行回收;新生代复制算法、老年代标记-压缩;垃圾收集的过程中会Stop The World(服务暂停,简称STW
在这里插入图片描述在这里插入图片描述
参数控制:-XX:+UseSerialGC 串行收集器

单线程执行回收操作,回收期间暂停所有应用线程的执行,client模式下的默认回收器,通过-XX:+UseSerialGC命令行可选项强制指定。参数可以设置使用新生代串行和老年代串行回收器

年轻代的回收算法(Minor Collection)
把Eden区的存活对象移到To区,To区装不下直接移到年老代,把From区的移到To区,To区装不下直接移到年老代,From区里面年龄很大的升级到年老代。 回收结束之后,Eden和From区都为空,此时把From和To的功能互换,From变To,To变From,每一轮回收之前To都是空的。设计的选型为复制。

年老代的回收算法(Full Collection)
年老代的回收分为三个步骤,标记(Mark)、清除(Sweep)、合并(Compact)。标记阶段把所有存活的对象标记出来,清除阶段释放所有死亡的对象,合并阶段 把所有活着的对象合并到年老代的前部分,把空闲的片段都留到后面。设计的选型为合并,减少内存的碎片。

简单来说:串行回收是单线程操作回收的,用在单核CPU。

ParNew收集器
ParNew收集器(并行收集器)其实就是Serial收集器的多线程版本。新生代并行,老年代串行;新生代复制算法、老年代标记-压缩
在这里插入图片描述在这里插入图片描述
参数控制:
-XX:+UseParNewGC ParNew收集器
-XX:ParallelGCThreads 限制线程数量

并行回收器在串行回收器基础上做了改进,他可以使用多个线程同时进行垃圾回收,对于计算能力强的计算机而言,可以有效的缩短垃圾回收所需的实际时间。

ParNew回收器是一个工作在新生代的垃圾收集器,他只是简单的将串行回收器多线程快他的回收策略和算法和串行回收器一样。

使用-XX:+UseParNewGC 新生代ParNew回收器,老年代则使用市行回收器。ParNew回收器工作时的线程数量可以使用-XX:ParaleiGCThreads参数指定,一般最好和计算机的CPU相当,避免过多的栽程影响性能。

简单来说:并行回收是多线程操作回收的,用在多核CPU。

ParallelScavenge收集器

Parallel Scavenge收集器类似ParNew收集器,Parallel收集器更关注系统的吞吐量。可以通过参数来打开自适应调节策略,虚拟机会根据当前系统的运行情况收集性能监控信息,动态调整这些参数以提供最合适的停顿时间或最大的吞吐量;也可以通过参数控制GC的时间不大于多少毫秒或者比例;新生代复制算法、老年代标记-压缩
在这里插入图片描述
在这里插入图片描述
参数控制: -XX:+UseParallelGC 使用Parallel收集器+ 老年代串行

ParallelOld收集器
Parallel Old是Parallel Scavenge收集器的老年代版本,使用多线程和“标记-整理”算法。这个收集器是在JDK 1.6中才开始提供
在这里插入图片描述
在这里插入图片描述
参数控制: -XX:+UseParallelOldGC 使用Parallel收集器 + 老年代并行

老年代ParallelOldGC回收器也是一种多线程的回收器,和新生代的
ParallelGC回收器一样,也是一种关往吞吐量的回收器,他使用了标记压缩算法进行实现。

参数控制:
-XX:+UseParallelOldGC 进行设置
-XX:+ParallelCThread 也可以设置垃圾收集时的线程教量。

CMS收集器

CMS(Concurrent Mark Sweep)收集器是一种以获取最短回收停顿时间为目标的收集器。目前很大一部分的Java应用都集中在互联网站或B/S系统的服务端上,这类应用尤其重视服务的响应速度,希望系统停顿时间最短,以给用户带来较好的体验。
在这里插入图片描述
从名字(包含“Mark Sweep”)上就可以看出CMS收集器是基于“标记-清除”算法实现的,它的运作过程相对于前面几种收集器来说要更复杂一些,整个过程分为4个步骤,包括:
在这里插入图片描述
在这里插入图片描述
其中初始标记、重新标记这两个步骤仍然需要“Stop The World”。初始标记仅仅只是标记一下GC Roots能直接关联到的对象,速度很快,并发标记阶段就是进行GC Roots Tracing的过程,而重新标记阶段则是为了修正并发标记期间,因用户程序继续运作而导致标记产生变动的那一部分对象的标记记录,这个阶段的停顿时间一般会比初始标记阶段稍长一些,但远比并发标记的时间短。

由于整个过程中耗时最长的并发标记和并发清除过程中,收集器线程都可以与用户线程一起工作,所以总体上来说,CMS收集器的内存回收过程是与用户线程一起并发地执行。老年代收集器(新生代使用ParNew)

优点: 并发收集、低停顿
缺点: 产生大量空间碎片、并发阶段会降低吞吐量

参数控制:
-XX:+UseConcMarkSweepGC 使用CMS收集器
-XX:+ UseCMSCompactAtFullCollection Full GC后,进行一次碎片整理;整理过程是独占的,会引起停顿时间变长
-XX:+CMSFullGCsBeforeCompaction 设置进行几次Full GC后,进行一次碎片整理
-XX:ParallelCMSThreads 设定CMS的线程数量(一般情况约等于可用CPU数量)
在这里插入图片描述
简单来说:CMS就是并行回收的升级版。

G1收集器
G1是目前技术发展的最前沿成果之一,HotSpot开发团队赋予它的使命是未来可以替换掉JDK1.5中发布的CMS收集器。与CMS收集器相比G1收集器有以下特点:

空间整合,G1收集器采用标记整理算法,不会产生内存空间碎片。分配大对象时不会因为无法找到连续空间而提前触发下一次GC。

可预测停顿,这是G1的另一大优势,降低停顿时间是G1和CMS的共同关注点,但G1除了追求低停顿外,还能建立可预测的停顿时间模型,能让使用者明确指定在一个长度为N毫秒的时间片段内,消耗在垃圾收集上的时间不得超过N毫秒,这几乎已经是实时Java(RTSJ)的垃圾收集器的特征了。

上面提到的垃圾收集器,收集的范围都是整个新生代或者老年代,而G1不再是这样。使用G1收集器时,Java堆的内存布局与其他收集器有很大差别,它将整个Java堆划分为多个大小相等的独立区域(Region),虽然还保留有新生代和老年代的概念,但新生代和老年代不再是物理隔阂了,它们都是一部分(可以不连续)Region的集合。
在这里插入图片描述
G1的新生代收集跟ParNew类似,当新生代占用达到一定比例的时候,开始出发收集。和CMS类似,G1收集器收集老年代对象会有短暂停顿。

收集步骤:

1、标记阶段,首先初始标记(Initial-Mark),这个阶段是停顿的(Stop the World Event),并且会触发一次普通Mintor GC。对应GC log:GC pause (young) (inital-mark)

2、Root Region Scanning,程序运行过程中会回收survivor区(存活到老年代),这一过程必须在young GC之前完成。

3、Concurrent Marking,在整个堆中进行并发标记(和应用程序并发执行),此过程可能被young GC中断。在并发标记阶段,若发现区域对象中的所有对象都是垃圾,那个这个区域会被立即回收(图中打X)。同时,并发标记过程中,会计算每个区域的对象活性(区域中存活对象的比例)。
在这里插入图片描述
4、Remark, 再标记,会有短暂停顿(STW)。再标记阶段是用来收集 并发标记阶段 产生新的垃圾(并发阶段和应用程序一同运行);G1中采用了比CMS更快的初始快照算法:snapshot-at-the-beginning (SATB)。

5、Copy/Clean up,多线程清除失活对象,会有STW。G1将回收区域的存活对象拷贝到新区域,清除Remember Sets,并发清空回收区域并把它返回到空闲区域链表中。
在这里插入图片描述
6、复制/清除过程后。回收区域的活性对象已经被集中回收到深蓝色和深绿色区域。
在这里插入图片描述

常用的收集器组合

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

序号新生代GC策略老年老代GC策略说明
1SerialSerial OldSerial和Serial Old都是单线程进行GC,特点就是GC时暂停所有应用线程。
2SerialCMS+Serial OldCMS(Concurrent Mark Sweep)是并发GC,实现GC线程和应用线程并发工作,不需要暂停所有应用线程。另外,当CMS进行GC失败时,会自动使用Serial Old策略进行GC。
3ParNewCMS使用 -XX:+UseParNewGC选项来开启。ParNew是Serial的并行版本,可以指定GC线程数,默认GC线程数为CPU的数量。可以使用-XX:ParallelGCThreads选项指定GC的线程数。如果指定了选项 -XX:+UseConcMarkSweepGC选项,则新生代默认使用ParNew GC策略。
4ParNewSerial Old使用-XX:+UseParNewGC选项来开启。新生代使用ParNew GC策略,年老代默认使用Serial Old GC策略。
5Parallel ScavengeSerial OldParallel Scavenge策略主要是关注一个可控的吞吐量:应用程序运行时间 / (应用程序运行时间 + GC时间),可见这会使得CPU的利用率尽可能的高,适用于后台持久运行的应用程序,而不适用于交互较多的应用程序。
6Parallel ScavengeParallel OldParallel Old是Serial Old的并行版本
7G1GCG1GC-XX:+UnlockExperimentalVMOptions -XX:+UseG1GC #开启;-XX:MaxGCPauseMillis=50 #暂停时间目标;-XX:GCPauseIntervalMillis=200 #暂停间隔目标; -XX:+G1YoungGenSize=512m #年轻代大小; -XX:SurvivorRatio=6 #幸存区比例

JVM参数调优(堆调优)

默认的堆的老年代和新生代的比例是2:1,新生代的Eden和From和To比例是8:1:1

在实际情况中,具体的分配还是需要根据硬件的配置或根据项目的情况来定的,没有说非常固定的配比。

下方示图举例:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

监控工具

在这里插入图片描述
在这里插入图片描述
IDEA有支持监控的插件,使用起来很方便,可以去了解一下。

JVM参数调总结

在JVM启动参数中,可以设置跟内存、垃圾回收相关的一些参数设置,默认也不影响,但你向让Server得到更好的性能,就必须通过设置JVM参数来改善。

  • GC的时间足够的短
  • GC的次数足够的少
  • 发生Full GC的周期足够的长

要想GC时间短必须要一个更小的堆,要向GC次数少,又必须保证一个更大的堆,我们只能取其平衡。

一般来说JVM堆的设置,通过-Xms -Xmx限定其最小、最大值,为了防止垃圾收集器在最小、最大之间收缩堆而产生额外的时间,我们通常把最大、最小设置为相同的值。

其次就是关于年轻代和年老代将根据默认的比例(1:2)分配堆内存,可以通过调整二者之间的比率NewRadio来调整二者之间的大小,也可以针对回收,比如年轻代,通过 -XX:newSize -XX:MaxNewSize来设置其绝对大小。同样,为了防止年轻代的堆收缩,我们通常会把-XX:newSize -XX:MaxNewSize设置为同样大小。

Web服务器内存溢出

找到对应的配置文件,用JVM参数调整即可。

举个栗子:Tomcat服务器内存溢出在catalina.sh 修改JVM堆内存大小。
JAVA_OPTS="-server -Xms800m -Xmx800m -XX:PermSize=256m -XX:MaxPermSize=512m -XX:MaxNewSize=512m"

Finally!

实践才是真理,你可以启动一个web项目,给Tomcat配置JVM参数,然后用Jmeter去做压力测试。去测试一下吧!

这篇博客可以说是我的一个学习和回顾吧,参考了多方的资料:百度百科、百度搜索、极客时间、CSDN、Wechat公众号"纯洁的微笑"、线下文档PPT等渠道资料。

如果你想更全面的对JVM深入了解,可以搜索Wechat公众号"纯洁的微笑",大佬的JVM系列挺全的!

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值