文章目录
- jvm介绍
- jvm内存模型
- jvm内存分配参数
- jvm堆中存储对象:
- 对象在堆中创建分配内存过程
- jvm 堆垃圾收集器
- 垃圾回收算法
- 标记阶段
- 引用计数算法
- 可达性分析算法
- 清除阶段
- 标记清除算法
- 复制算法
- 标记压缩算法
- 实际jvm参数
- 实战jvm调优
- jvm常用命令
- 常用工具
jvm介绍
Java虚拟机(JVM)是Java程序运行的核心组件之一,它负责将Java字节码(Java编译后的中间代码)解释或编译为特定平台上的机器码,并在运行时提供内存管理、垃圾回收、线程管理等功能。
以下是JVM的主要特点和组成部分:
1.字节码执行环境: JVM是一个能够执行Java字节码的虚拟机,它将编译后的Java源代码转换为字节码,然后在运行时加载并执行这些字节码。
2.跨平台性: 由于Java字节码是与平台无关的中间代码,所以Java程序可以在任何安装了JVM的平台上运行,实现了“一次编写,到处运行”的特性。
3.即时编译器(JIT): JVM通常包含即时编译器,它负责将热点代码(经常执行的代码)编译为本地机器码,以提高程序的执行效率。
4.垃圾回收器: JVM提供了自动内存管理功能,包括垃圾回收机制,用于回收不再使用的内存对象,防止内存泄漏和碎片化。
5.类加载器: JVM使用类加载器加载Java类文件,并在运行时动态链接和初始化这些类。
6.运行时数据区域: JVM将内存分为多个不同的区域,包括堆、栈、方法区等,用于存储不同类型的数据和执行不同的操作。
7.多线程支持: JVM支持多线程执行,它提供了线程管理和同步机制,使得Java程序能够有效地利用多核处理器和并发编程模型。
jvm内存模型
Java虚拟机(JVM)的内存模型定义了Java程序在运行时如何使用内存,并将内存分为不同的区域,每个区域用于存储不同类型的数据。以下是Java虚拟机的内存模型中常见的区域:
程序计数器:
程序计数器是一块较小的内存区域,它存储了当前线程正在执行的字节码指令的地址或索引。在多线程环境下,每个线程都有一个独立的程序计数器,用于记录线程当前执行的位置。
在Java虚拟机规范中,程序计数器是线程私有的,不会发生线程间的共享。
Java虚拟机栈:
Java虚拟机栈是每个线程私有的内存区域,用于存储方法调用的栈帧(Stack Frame)。
每个方法调用都会创建一个栈帧,栈帧包含了方法的局部变量表、操作数栈、动态链接、方法返回地址等信息。如果栈帧空间不足,或者方法的递归调用层次过深,就会抛出栈溢出异常(StackOverflowError)。
堆(Heap):
堆是Java虚拟机中最大的一块内存区域,用于存储对象实例和数组。
所有的对象实例都在堆上分配内存,通过new关键字创建的对象都存储在堆上。
堆是所有线程共享的内存区域,因此在多线程环境下需要进行同步操作。
方法区:
方法区是用于存储类的元信息、静态变量、常量、编译后的代码等数据的内存区域。
方法区是所有线程共享的内存区域,其中包含了所有类的信息,如类名、方法签名、字段描述等。在Java 8及之前的版本中,方法区也被称为永久代。运行时常量池运行时常量池是方法区的一部分,用于存储编译时生成的常量和符号引用。运行时常量池包含了类、方法和字段的符号引用以及字面量(如字符串常量)等信息。
本地方法栈:
本地方法栈与Java虚拟机栈类似,用于执行本地方法(Native Method)的栈帧。
本地方法栈存储了调用本地方法时所需的参数和局部变量,并负责调用本地方法库中的函数。
线程共享区域:堆、方法区
线程私有区域:栈、虚拟机栈、程数计数器
jvm内存分配参数
jvm堆中存储对象:
1.对象实例: 所有通过 new 关键字创建的对象实例都存储在堆中。无论是在堆上直接分配还是间接分配(例如通过栈上的引用指向堆中的对象),最终都会在堆中分配内存。
2.数组: 所有的数组对象也存储在堆中。数组是对象的一种特殊形式,它们同样需要在堆中分配内存。
3.垃圾回收信息: 堆中包含了垃圾回收器所需的信息,用于标记和清理不再使用的对象。这些信息包括对象的引用关系、对象的存活状态等。
4.线程共享数据: 在某些情况下,Java虚拟机会将一些线程共享的数据(例如字符串常量池)存储在堆中。
5.Java虚拟机本身的数据结构: Java虚拟机会在堆中存储一些自身的数据结构,用于管理堆的分配和回收。
Java堆既可以被实现成固定大小的,也可以是可扩展的,不过当前主流的Java虚拟机都是按照可扩展来实现的(通过参数-Xmx和-Xms设定)。如果在Java堆中没有内存完成实例分配,并且堆也无法再扩展时,Java虚拟机将会抛出OutOfMemoryError异常。
Java堆区相关的控制参数:
-Xms设置堆的最小空间大小。
-Xmx设置堆的最大空间大小。
-XX:NewSize设置新生代最小空间大小。
-XX:MaxNewSize设置新生代最大空间大小。
-Xss设置每个线程的堆栈大小。
没有直接设置老年代的参数,但是可以设置堆空间大小和新生代空间大小两个参数来间接控制。
对象在堆中创建分配内存过程
(1)new的对象先放Eden(伊甸园)区。此区有大小限制。
(2)当Eden的空间填满时,程序又需要创建对象,JVM的垃圾回收器将对Eden区进行垃圾回收(Minor GC), 将Eden区中的不再被其他对象所引用的对象进行销毁。再加载新的对象放到Eden区
(3)然后将Eden中的剩余对象移动到幸存者0区。
(4)如果再次触发垃圾回收,此时上次幸存下来的放到幸存者0区的,如果没有回收,就会放到幸存者1区。
(5)如果再次经历垃圾回收,此时会重新放回幸存者0区,接着再去幸存者1区
(6)啥时候能去养老区呢?可以设置次数。默认是15次。
可以设置参数:-XX:MaxTenuringThreshold= 进行设置
(7)如果old区可以放下对象,则直接分配对象到old区。
(8)old区如果也没有空间分配对象,直接full gc来释放空间,来分配内存。
(9)如果full gc之后还没有足够的空间分配对象,则直接报内存溢出。
jvm 堆垃圾收集器
如果两个收集器之间存在连线,就说明它们可以搭配使用。它们说在的区域则表示这个收集器属于新生代收集器还是老年代收集器。其中Serial(串行)、Parallel(并行)
1、Serial收集器
Serial收集器是最基础、历史最悠久的收集器,是HotSpot虚拟机新生代收集器的唯一选择。这个收集器是一个单线程工作的收集器。这个收集器再进行垃圾收集时,必须停掉所有的工作线程,直到收集完成。这个停掉的工作是后台自己执行的,用户并不知情。这个是我们无法接受的。
从上面的种种缺点看去似乎这个收集器已经被时代抛弃,没有优点。但是它依旧是HotSpot虚拟机运行再客户端模式下默认的新生代收集器。因为它有一个很大的优点简单而高效。再内存环境受限的环境,它是所有收集器里面的额外内存消耗最小的。一般来说分配给虚拟机管理的内存一般不会特别大,对于几十兆甚至一两百兆的新生代,Serial垃圾收集器的停顿时间完全可以控制再可接受范围。所以, Serial收集器对于运行在客户端模式下的虚拟机来说是一个很好的选择。
2、ParNew收集器
ParNew收集器实质上是Serial收集器的多线程并行版本,出来可以同时多条线程进行垃圾收集外,其余行为几乎与Serial收集器一致。
这个收集器明明没有太大的创新为什么能有怎么重要的地位呢,那就是因为它的搭档CMS收集器。CMS第一款真正意义上支持并发的垃圾收集器,它首次 实现了让垃圾收集线程与用户线程同时工作。但这个收集器只能Serial和ParNew这两个新生代收集器配合使用。这样奠定了ParNew的地位。随着G1收集器的产生,这一组合被逐渐取代,官方希望它能完全被G1所取代,ParNew和CMS从此只能互相搭配使用,再也没有其他收集器能够和它们配合了。也可以理解为从此以后,ParNew合并入CMS,成为它专门处理新生代的组成部分。ParNew可以说是HotSpot虚拟机中第一款退出历史舞台的垃圾收集器。
3、Parallel Scavenge收集器
Parallel Scavenge收集器是一款新生代收集器,它基于标记复制算法实现。也是能够并行收集的多线程收集器。它独特的点在于它的注重点与其他收集器不同,CMS等收集器的关注点是尽可能的缩短垃圾收集时用户线程的停顿时间,而Parallel Scavenge的注重点是一个可控的吞吐量。
完成程序的运算任务,适合在后台不需要过多交互的程序。
这个收集器提供了两个参数用于精确控制吞吐量,分别是控制最大垃圾收集停顿时间
-XX:MaxGCPauseMillis参数以及直接设置吞吐量大小的-XX:GCTimeRatio参数。
4、Serial Old收集器
Serial Old是Serial收集器的老年版本,它是一个单线程收集器,使用标记整理算法。这个收集器的主要作用是再客户端模式下的HotSpot虚拟机使用。再服务端下一种是JDK5即以前与ParallelScavenge收集器搭配使用,另一种就是作为CMS收集器发生失败时的后背预案。
5、Parallel Old收集器
parallel Old是parallel Scavenge收集器的老年版本,支持多线程并发收集,基于标记整理算法实现。出现在JDK6后。当注重吞吐量或者处理器资源稀缺的时候,都会优先考虑 parallel Scavenge+Parallel Old收集器这个组合。
6、CMS收集器 (并发收集、低停顿)
CMS(Concurrent Mark Sweep)收集器是以获取最短回收停顿时间的为目标的收集器。注重服务的响应速度,希望系统停顿时间尽可能短,以给用户更好的交互体验。这个收集器是基于标记清除算法实现的。用于老年代的收集。
收集过程有四个阶段:1、初始标记 2、并发标记 3、重新标记 4、并发清除
四个阶段中初始标记和重新标记仍需要暂停所有的用户线程(Stop The World),但为什么说这个收集器也暂停了所有的线程,为什么还能做到停顿时间短呢。因为初始标记阶段只是标记GC Roots能直接关联的对象,这个过程很快。而并发标记时才进行沿GC Roots遍历所有对象,这个工作量说不小的,但这个过程并没有停顿用户线程,而是与其并发执行,如果再过程出现对象引用关系改变,则使用增量更新的方法将其标记。待重新标记阶段就是为了解决这个并发过程中因为改变而被标记的对象。这个阶段是要暂停用户线程的,但这部分的工作量也不大。最后全部标记玩就进入了并发清除的阶段了。这部分也是与用户线程并发进行的。
从整体上看来耗时长的并发标记和并发清除都没有暂停用户线程,所有可以说:从总体上来说,CMS收集器的内存回收过程是与用户线程一起并发执行的。
缺点:
1.CMS收集器对CPU资源很敏感。它虽然不会导致用户线程停顿,但也会因为占用一部分线程导致应用程序变慢。它默认启动的回收线程数是(核心线程数+3)/4,所以随着核心线程数的降低,CMS收集器的弊端会越来越明显。
2.CMS是一款基于“标记-清除”算法实现的收集器,这意味着收集结束时会有大量空间碎片产生。空间 碎片过多时,将会给大对象分配带来很大麻烦,往往会出现老年代还有很多剩余空间,但就是无法找 到足够大的连续空间来分配当前对象,所以会提前导致Full GC的到来。
3.CMS无法处理浮动垃圾(再清除阶段用户线程还在运行,产生的垃圾)。必须等到下次GC时才能清理,而且不能等到老年代满了后再清理,因为再清理过程中用户线程还在产生对象,所以要预留一定内存,提前开启垃圾清理。
G1收集器(Garbage First)
G1是一种“停顿时间模型”的收集器,它能指定时间N,确保消耗再垃圾收集上时间大概率不超过N毫秒的目的。G1收集器一改之前的分区收集思想,开创了面对局部收集的设计思路。它将java堆划分为多个大小相等的独立区域Region。它可以面对堆内存任何部分组成回收集进行回收。这个模型回收哪块的衡量标准是哪块Region垃圾最多,再N毫秒内回收收益最大。这就是G1收集器的Mixed GC模式。
再并发操作阶段,CMS收集器采用增量更新算法实现,而G1 收集器则是通过原始快照(SATB)算法来实现的。
收集过程:
初始标记:仅仅只是标记一下GC Roots能直接关联到的对象,需要暂停用户线程,但耗时很短。
并发标记:从GC Root开始对堆中对象进行可达性分析,递归扫描整个堆 里的对象图,找出要回收的对象,这阶段耗时较长,但可与用户程序并发执行。当对象图扫描完成以 后,还要重新处理SATB记录下的在并发时有引用变动的对象。 ·
最终标记:对用户线程做另一个短暂的暂停,用于处理并发阶段结束后仍遗留 下来的最后那少量的SATB记录。 ·
筛选回收:负责更新Region的统计数据,对各个Region的回 收价值和成本进行排序,根据用户所期望的停顿时间来制定回收计划,可以自由选择任意多个Region 构成回收集,然后把决定回收的那一部分Region的存活对象复制到空的Region中,再清理掉整个旧 Region的全部空间。这里的操作涉及存活对象的移动,是必须暂停用户线程,由多条收集器线程并行 完成的。
毫无疑问,可以由用户指定期望的停顿时间是G1收集器很强大的一个功能,设置不同的期望停顿 时间,可使得G1在不同应用场景中取得关注吞吐量和关注延迟之间的最佳平衡。当然这个值也得是一个可能的值,要是太低了,就会导致每次回收不了多少,回收次数增加。
垃圾回收算法
标记阶段
引用计数算法
所谓的引用计数法就是给每个对象一个引用计数器,每当有一个地方引用它时,计数器就会加1;当引用失效时,计数器的值就会减1;任何时刻计数器的值为0的对象就是不可能再被使用的。
优点:
1.可即时回收垃圾:在该方法中,每个对象始终知道自己是否有被引用,当被引用的数值为0时,对象马上可以把自己当作空闲空间链接到空闲链表。
2.最大暂停时间短。
3.没有必要沿着指针查找
缺点:
1.计数器值的增减处理非常繁重
2.计算器需要占用很多位。
3.实现繁琐。
4.循环引用无法回收。
可达性分析算法
所谓的可达性就是通过一系列称为“GC Roots”的对象为起点,从这些节点开始向下搜索,搜索走过的路径称为引用链,当一个对象到GC Roots没有任何引用链相连(用图论的话来说,就是GC Roots到这个对象不可达)时,则说明此对象是不可用的。
GC Root对象包括以下几种类型:
虚拟机栈(Java Virtual Machine Stacks)中的本地变量: 虚拟机栈中的本地变量引用的对象是GC Root对象。这些本地变量通常是Java方法的参数、局部变量等。
方法区(Method Area)中的静态变量: 方法区中的静态变量引用的对象也是GC Root对象。静态变量是类级别的变量,它们存储在方法区中,因此直接引用的对象是GC Root对象。
方法区中的常量引用: 方法区中的常量引用的对象也是GC Root对象。常量池中的字符串常量、类名等都是被直接引用的对象。
本地方法栈(Native Method Stack)中的本地变量: 本地方法栈中的本地变量引用的对象也是GC Root对象。本地方法栈中存储的是本地方法调用的栈帧,其中的本地变量引用的对象是GC Root对象。
GC Root对象是垃圾回收的起点,垃圾回收器会从这些对象开始遍历,通过对象之间的引用关系来确定哪些对象是可达的,哪些对象是不可达的,从而进行垃圾回收操作。如果一个对象不是GC Root对象的直接或间接引用,则说明它是可被回收的,垃圾回收器会将其回收释放内存。
清除阶段
标记清除算法
此算法就是非常基础和场景的垃圾收集算法,分为两个步骤,第一步是标记,第二是清除。当堆中的有效空间被消耗殆尽时,就会STW。
标记:垃圾收集器开始从引用根节点开始遍历,标记所有被引用的对象,一般在对象头中记录为可达对象。
清除:垃圾收集器对堆内存从头到尾进行线性的遍历,如果发现某个对象在其header中没有标记为可达对象,就执行清除。
这里补充说明一下清除的操作,所谓的清除并非是真的置空,而是把需要清除的对象地址保存在空闲的地址列表里。下次有新对象需要加载时,判断垃圾的位置空间是否足够,如果够,就存放。
此算法的缺点是清理之后的空间内存是不连续的,会产生内存碎片,需要维护一个空闲列表来记录那些区域是可用的。也就是说如果使用的垃圾收集器是采用的标记清除算法,那么在分配对象的时候就是使用空闲列表的方式。
复制算法
回收前:
回收后:
复制(Copying)算法将可用内存被容量划分为大小相等的两块,每次只使用其中的一块。当这一块的内存用完了,就将还存活着的对象复制到另外一块上面,然后再把已使用过的内存空间一次清理掉。
复制算法的优点:
每次都是对整个半区进行内存回收,内存分配时也就不用考虑内存碎片等复杂情况。只要移动堆顶指针,按顺序分配内存即可,实现简单,运行高效。
复制算法的缺点
将内存缩小为原来的一半,未免太高了一点。
标记压缩算法
在复制算法的基础上和老年代存活对象多的特性上,演变出了标记压缩算法,这个算法继承了标记清除算法和复制算法的优点,但相应的也会有一定的缺点。
执行过程:
第一阶段和标记-清除算法一样,从根节点开始标记所有被引用的对象。
第二阶段将所有存活的对象压缩到内存的一端,按照顺序排列。
第三阶段就是清除边界外的空间。
从执行过程可以看出来,此算法的最终效果等同于标记清除算法执行之后,再进行一次内存碎片整理,所以又可以称为标记-清除-压缩算法。和标记清除算法的区别是,标记清除是非移动式,而标记压缩是移动式的。因为有了后面的压缩操作,所以在创建对象分配空间的时候无需维护空闲列表。
优点:刚才也说了,标记压缩算法集众家之所长,结合了标记清除和复制算法的优点,比如:消除了标记清除中内存碎片的问题,消除了复制中2倍内存的问题。
缺点:缺点就是效率问题,以及复制算法的缺点。效率问题就是它比复制算法多了一个标记的阶段,比清除多了一个内存整理的阶段复制算法的缺点就是需要移动对象的引用地址(Java中采用直接指针来进行访问定位,如果采用句柄那就不需要移动,但却需要单独开辟空间去维护句柄地址)。
实际jvm参数
-Xms 初始堆大小 物理内存的1/64(<1GB) 默认(MinHeapFreeRatio参数可以调整)空余堆内存小于40%时,JVM就会增大堆直到-Xmx的最大限制.
-Xmx 最大堆大小 物理内存的1/4(<1GB) 默认(MaxHeapFreeRatio参数可以调整)空余堆内存大于70%时,JVM会减少堆直到 -Xms的最小限制
-Xmn 年轻代大小(1.4or lator) 注意:此处的大小是(eden+ 2 survivor space).与jmap -heap中显示的New gen是不同的。 整个堆大小=年轻代大小 + 年老代大小 + 持久代大小. 增大年轻代后,将会减小年老代大小.此值对系统性能影响较大,Sun官方推荐配置为整个堆的3/8
-XX:NewSize 设置年轻代大小(for 1.3/1.4)
-XX:MaxNewSize 年轻代最大值(for 1.3/1.4)
-XX:PermSize 设置持久代(perm gen)初始值 物理内存的1/64
-XX:MaxPermSize 设置持久代最大值 物理内存的1/4
-Xss 每个线程的堆栈大小 JDK5.0以后每个线程堆栈大小为1M,以前每个线程堆栈大小为256K.更具应用的线程所需内存大小进行 调整.在相同物理内存下,减小这个值能生成更多的线程.但是操作系统对一个进程内的线程数还是有限制的,不能无限生成,经验值在3000~5000左右 一般小的应用, 如果栈不是很深, 应该是128k够用的 大的应用建议使用256k。这个选项对性能影响比较大,需要严格的测试。(校长) 和threadstacksize选项解释很类似,官方文档似乎没有解释,在论坛中有这样一句话:"” -Xss is translated in a VM flag named ThreadStackSize” 一般设置这个值就可以了。
-XX:ThreadStackSize Thread Stack Size 线程栈的大小,JDK5.0以后每个线程堆栈大小默认为1M,以前每个线程堆栈大小为256K;可以通过jvm参数-Xss来设置;注意-Xss是jvm的非标准参数,不强制所有平台的jvm都支持
-XX:NewRatio 年轻代(包括Eden和两个Survivor区)与年老代的比值(除去持久代) -XX:NewRatio=4表示年轻代与年老代所占比值为1:4,年轻代占整个堆栈的1/5 Xms=Xmx并且设置了Xmn的情况下,该参数不需要进行设置。
-XX:SurvivorRatio Eden区与Survivor区的大小比值 设置为8,则两个Survivor区与一个Eden区的比值为2:8,一个Survivor区占整个年轻代的1/10
-XX:LargePageSizeInBytes 内存页的大小不可设置过大, 会影响Perm的大小 =128m
-XX:+UseFastAccessorMethods 原始类型的快速优化
-XX:+DisableExplicitGC 关闭System.gc() 这个参数需要严格的测试
-XX:MaxTenuringThreshold 垃圾最大年龄 如果设置为0的话,则年轻代对象不经过Survivor区,直接进入年老代. 对于年老代比较多的应用,可以提高效率.如果将此值设置为一个较大值,则年轻代对象会在Survivor区进行多次复制,这样可以增加对象再年轻代的存活 时间,增加在年轻代即被回收的概率 该参数只有在串行GC时才有效.
-XX:+AggressiveOpts 加快编译
-XX:+UseBiasedLocking 锁机制的性能改善
-Xnoclassgc 禁用垃圾回收
-XX:SoftRefLRUPolicyMSPerMB 每兆堆空闲空间中SoftReference的存活时间 1s 每兆堆空闲空间中SoftReference的存活时间
-XX:PretenureSizeThreshold 对象超过多大是直接在旧生代分配 0 单位字节 新生代采用Parallel Scavenge GC时无效 另一种直接在旧生代分配的情况是大的数组对象,且数组中无外部引用对象.
-XX:TLABWasteTargetPercent TLAB占eden区的百分比 1%
-XX:+CollectGen0First FullGC时是否先YGC FALSE
-XX:+UseParallelGC Full GC采用parallel MSC (此项待验证) 选择垃圾收集器为并行收集器.此配置仅对年轻代有效.即上述配置下,年轻代使用并发收集,而年老代仍旧使用串行收集.(此项待验证)
-XX:+UseParNewGC 设置年轻代为并行收集 可与CMS收集同时使用 JDK5.0以上,JVM会根据系统配置自行设置,所以无需再设置此值
-XX:ParallelGCThreads 并行收集器的线程数 此值最好配置与处理器数目相等 同样适用于CMS
-XX:+UseParallelOldGC 年老代垃圾收集方式为并行收集(Parallel Compacting) 这个是JAVA 6出现的参数选项
-XX:MaxGCPauseMillis 每次年轻代垃圾回收的最长时间(最大暂停时间) 如果无法满足此时间,JVM会自动调整年轻代大小,以满足此值.
-XX:+UseAdaptiveSizePolicy 自动选择年轻代区大小和相应的Survivor区比例 设置此选项后,并行收集器会自动选择年轻代区大小和相应的Survivor区比例,以达到目标系统规定的最低相应时间或者收集频率等,此值建议使用并行收集器时,一直打开.
-XX:GCTimeRatio 设置垃圾回收时间占程序运行时间的百分比 公式为1/(1+n)
-XX:+ScavengeBeforeFullGC Full GC前调用YGC TRUE Full GC前调用 年轻代GC
-XX:+PrintGC
-XX:+PrintGCDetails
-XX:+PrintGCTimeStamps
-XX:+PrintGC:PrintGCTimeStamps 可与-XX:+PrintGC -XX:+PrintGCDetails混合使用
-XX:+PrintGCApplicationStoppedTime 打印垃圾回收期间程序暂停的时间.可与上面混合使用 输出形式:Total time for which application threads were stopped: 0.0468229 seconds
-XX:+PrintGCApplicationConcurrentTime 打印每次垃圾回收前,程序未中断的执行时间.可与上面混合使用 输出形式:Application time: 0.5291524 seconds
-XX:+PrintHeapAtGC 打印GC前后的详细堆栈信息
-Xloggc:filename 把相关日志信息记录到文件以便分析. 与上面几个配合使用
-XX:+PrintClassHistogram garbage collects before printing the histogram.
-XX:+PrintTLAB 查看TLAB空间的使用情况
XX:+PrintTenuringDistribution 查看每次minor GC后新的存活周期的阈值 Desired survivor size 1048576 bytes, new threshold 7 (max 15) new threshold 7即标识新的存活周期的阈值为7。
实战jvm调优
jvm常用命令
jps:查看java进程
jinfo:实时查看和调整JVM配置参数
jinfo -flag name PID 查看某个java进程的name属性值
比如:
jinfo -flag MaxHeapSize PID 查看最大堆内存大小
jinfo -flag Use G1GC PID 查看是否使用G1GC
调整用法
参数只有被标记为Manageable的flags可以被实时修改
jinfo -flag [+|-] PID
jinfo -flag = PID
jstat:查看虚拟机性能统计信息,查看类装载信息
jstat -class PID 1000 10 查看某个Java进程的类装载信息,每1000毫秒输出一次,供输出10次
查看垃圾收集信息
jstat -gc PID 1000 10 查看某个Java进程的垃圾收集信息,每1000毫秒输出一次,供输出10次
jstack:查看线程堆栈信息
jmap:生成堆转储快照
打印出堆内存相关信息:jmap -heap PID
dump出堆内存相关信息:
jmap -dump:format=b,file=xxx.hprof PID
dump下来的文件直接看有些费力,可以结合MAT工具分析。一般在开发中,JVM参数可以通过添加参数,这样内存溢出时,会自动dump该文件
-XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=heap.hprof
常用工具
jconsole
JConsole工具是JDK自带的可视化监控工具,查看Java应用程序的运行情况,监控堆信息,永久区使用情况,类加载情况等
jvisualvm
可以监控本地的java进程的cpu,类,线程等,也可以监控远端java进程,比如监控远端tomcat
如果是连接远端tomcat[也可以是任意Java进程],比如部署在阿里云服务器上的tomcat,可以按照以下步骤:
(1)在visualvm中选中“远程”,右击“添加”
(2)主机名上写服务器的ip地址,比如39.100.39.63
(3)右击该主机,添加“JMX”,也就是通过JMX技术具体监控远端服务器哪个Java进程
(4)要想让服务器上的tomcat被连接,需要改一下Catalina.sh这个文件
注意端口不要和服务器上的有冲突。
JAVA_OPTS="$JAVA_OPTS -Dcom.sun.management.jmxremote -
Djava.rmi.server.hostname=39.100.39.63 -Dcom.sun.management.jmxremote.port=8998 -
Dcom.sun.management.jmxremote.ssl=false -
Dcom.sun.management.jmxremote.authenticate=true -
Dcom.sun.management.jmxremote.access.file=../conf/jmxremote.access -
Dcom.sun.management.jmxremote.password.file=../conf/jmxremote.password"
(5)在…/conf文件中添加两个文件jmxremote.access和jmxremote.password
jmxremote.access
guest readonly
manager readwrite
jmxremote.password
guest guest
manager manager
授予权限 :chomd 600 jmxremot
(6)将连接服务器地址改为公网ip地址
(7)查看端口监听情况
lsof -i:port #得到PID
netstat -antup | grep PID
(8)设置上述端口的防火墙策略和安全组策略
(9)启动远端java进程,
(10)在jmx 中输入端口,输入用户名和密码即可登录成功
内存分析工具 MAT
GC日志分析工具
要想分析日志的信息,首先得拿到GC日志文件,可以通过配置启动参数获取GC日志文件
XX:+PrintGCDetails
-XX:+PrintGCTimeStamps
-XX:+PrintGCDateStamps
-Xloggc:$CATALINA_HOME/logs/gc.log
在线查看工具
GCViewer