Q:jvm调优是在做什么?
A:通过修改jvm参数和更换合适的垃圾回收器从而达到提高垃圾回收效率、降低停顿时间,提高系统的吞吐量和响应速度。
从上面的问答中可以得到两个概念,jvm参数、垃圾回收器。
程序计数器
存储当前线程正在执行的Java方法的字节码指令地址的内存区域,作为一个指示器使用,不会进行内存分配,因此不存在内存溢出的问题。
由于程序计数器是线程私有的,每个线程都有独立的程序计数器。这确保了在多线程环境下,线程切换后能够正确地恢复到上一个线程执行的位置。程序计数器在线程切换时起到了保持状态的重要作用。
Java虚拟机栈
采用栈的数据结构来管理方法调用中的基本数据,先进后出(First In Last Out),每一个方法的调用使用一个栈帧(Stack Frame)来保存,虚拟机栈种保存着方法的局部变量、操作数栈(虚拟机在执行指令过程中用来存放临时数据的一块区域)、动态链接、方法返回地址(方法正常退出或异常退出的定义)和一些附加信息;局部变量的数量会影响到栈的深度
每个线程都会创建一个虚拟机栈,是线程私有的。栈的生命周期和线程同步一致,当线程线程结束,该虚拟机栈也就销毁了。
本地方法栈
本地方法就是Java程序调用非Java代码的接口,它的实现不是由Java语言实现。当Java程序需要和外间需要交互时,就可以选择使用本地方法。
本地方法栈(Native Method Stack)用于本地方法的调用,线程私有,它允许被实现成固定大小或是可动态扩展的内存大小。程序使用本地方法栈的具体做法是:在本地方法栈中登记native方法,在执行引擎执行时加载本地方法库。当某个线程调用一个本地方法时,它就进入了一个全新的且不受虚拟机限制的世界,它和虚拟机拥有同样的权限。
HotSpot直接将本地方法栈和虚拟机栈合二为一。
堆区
堆是一种动态内存分配机制,用于存储在程序运行过程中创建的对象。它是线程共享的,堆中的对象都需要考虑线程安全问题;存放引用数据类型的数据和new出来的的对象。
Jdk8后在逻辑上可以将堆区划分为新生代和老年代。新生代又可以划分为伊甸园区与幸存者区之间发生的内存整理行为是MinorGC,老年代发生的内存整理行为是MajorGC,整理堆区和方法区内存的行为是FullGC。
堆内存的大小可以在运行时进行调整,以满足程序的需求。调整过程涉及到如下三个概念;used指的是当前已使用的堆内存,total是java虚拟机已经分配的可用堆内存,max是java虚拟机可以分配的最大堆内存。
在 Java 中,堆内存的分配和管理由垃圾回收器(Garbage Collector,简称 GC)负责。当对象不再被引用时,GC 会自动回收其占用的内存空间。
Java服务端程序开发时,建议将-Xmx和-Xms设置为相同的值,这样在程序启动之后可使用的总内存就是最大内存,而无需向java虚拟机再次申请,减少了申请并分配内存时间上的开销,同时也不会出现内存过剩之后堆收缩的情况。
默认的新生代 ( Young ) 与老年代 ( Old ) 的比例的值为 1:2 ( 该值可以通过参数 –XX:NewRatio 来指定 ) , 即:新生代 ( Young ) = 1/3 的堆空间大小。老年代 ( Old ) = 2/3 的堆空间大小。其中,新生代 ( Young ) 被细分为 Eden 和 两个 Survivor 区域, Edem 和俩个 Survivor 区域比例是 = 8 : 1 : 1 ( 可以通过参数 – XX:SurvivorRatio 来设定 ) ,但是 JVM 每次只会使用 Eden 和其中的一块 Survivor 区域来为对象服务,所以 无论什么时候,总是有一块 Survivor 区域是空闲着的。
方法区
用于存储类型信息、方法信息、常量、及时编译器编译后的缓存代码。方法区与Java堆一样,是各个线程共享的内存区域,并且在JVM启动时被创建,物理内存空间可以不连续,随着JVM的关闭而释放。
在JDK 7以前,习惯上把方法区称为永久代(习惯上),而到了JDK8,废弃了永久代的概念,改用与JRockit、J9一样在本地内存中实现的元空间(Metaspace)来代替。这两个最大的区别就是:元空间不在虚拟机设置的内存中,而是使用本地内存。
jdk8 及之后使用参数 -XX:MetaspaceSize和-XX:MaxMetaspaceSize 来指定元空间的初始分配空间与最大可分配空间。在windows平台下,初始元空间的默认可分配空间是 21M ,最大可分配空间参数值为 -1(-1表示不限制)。
如果不指定元空间可分配最大内存空间,极端情况下虚拟机会耗尽所有的可用系统内存,如果元空间区发生溢出,虚拟机会抛出异常:OutOfMenoryError:Metaspace
设置jvm内存的常用参数
参数类型 |
参数举例 |
备注 |
堆最大值(max) |
-Xmx6M |
Xmx必须大于 2 MB,默认单位为字节(必须是1024的倍数)也可是指定单位k(K)、m(M)、g(G) |
堆初始值(total) |
-Xms6M |
Xms必须大于1MB,默认单位为字节(必须是1024的倍数)也可是指定单位k(K)、m(M)、g(G) |
Young区大小 |
-Xmn52M |
|
元空间的初始分配空间(MetaspaceSize) |
-XX:MetaspaceSize=128M |
|
元空间的最大可分配空间(MaxMetaspaceSize) |
-XX:MaxMetaspaceSize=512M |
|
Java虚拟机栈 |
-Xss128K |
默认单位为字节(必须是1024的倍数)也可是指定单位k(K)、m(M)、g(G),栈的大小直接决定了函数调用的最大可达深度,设置的值超过默认最大或者最小时设置失效 |
|
|
|
Jdk8常用的垃圾回收器
垃圾回收器 |
简介 |
目标 |
启用参数 |
适用场景 |
Serial收集器 |
Serial收集器是单线程的,在进行垃圾回收时会暂停所有用户线程,直到回收结束 |
响应速度优先 |
-XX:+UseSerialGC |
单核处理器或客户端模式下的虚拟机 |
ParNew收集器 |
ParNew是Serial收集器的多线程版本。它主要用于新生代垃圾回收,老年代则使用Serial Old |
响应速度优先 |
-XX:+UseParNewGC |
适用于多核处理器的server模式 |
Parallel Scavenge收集器 |
这是一个并行多线程收集器,Parallel Scavenge收集器可以与Serial Old或Parallel Old搭配使用 |
吞吐量优先 |
-XX:+UseParallelGC |
适用于服务端模式,适合响应时间要求不高的应用场景 |
CMS(Concurrent Mark Sweep)收集器 |
这是一种并发收集器。CMS收集器在垃圾回收时允许用户线程继续执行,但它在处理老年代时需要依赖新生代的收集器,如ParNew。 |
响应速度优先 |
-XX:+UseConcMarkSweepGC |
适用于追求低延迟的应用,例如互联网网站或者bs系统 |
G1(Garbage-First)收集器 |
G1收集器旨在平衡吞吐量和停顿时间。G1将堆内存划分为多个区域,并优先回收垃圾最多的区域,减少停顿时间 |
响应速度优先 |
-XX:+UseG1GC |
适用于大内存服务器和复杂应用 |
|
|
|
|
|